├── .babelrc ├── .eslintrc.json ├── .flowconfig ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── Procfile ├── README.md ├── config └── default.json.example ├── docs └── example_flow.md ├── flow-typed └── npm │ ├── babel-cli_vx.x.x.js │ ├── babel-eslint_vx.x.x.js │ ├── babel-plugin-syntax-async-functions_vx.x.x.js │ ├── babel-plugin-transform-flow-strip-types_vx.x.x.js │ ├── babel-plugin-transform-regenerator_vx.x.x.js │ ├── babel-polyfill_vx.x.x.js │ ├── babel-preset-es2015_vx.x.x.js │ ├── babel-preset-stage-3_vx.x.x.js │ ├── babel-watch_vx.x.x.js │ ├── body-parser_vx.x.x.js │ ├── config_vx.x.x.js │ ├── ejs_vx.x.x.js │ ├── eslint-config-standard_vx.x.x.js │ ├── eslint-plugin-flowtype_vx.x.x.js │ ├── eslint-plugin-import_vx.x.x.js │ ├── eslint-plugin-promise_vx.x.x.js │ ├── eslint-plugin-standard_vx.x.x.js │ ├── eslint_vx.x.x.js │ ├── express_v4.x.x.js │ ├── flow-bin_v0.x.x.js │ ├── jest-cli_vx.x.x.js │ ├── jsonwebtoken_vx.x.x.js │ ├── mongodb_vx.x.x.js │ ├── mongojs_vx.x.x.js │ ├── node-fetch_vx.x.x.js │ ├── pg-then_vx.x.x.js │ ├── pg_vx.x.x.js │ └── request_vx.x.x.js ├── images └── screenshot.png ├── jsconfig.json ├── package.json ├── public └── index.html ├── source ├── bot │ ├── artsy-api.js │ ├── contexts │ │ ├── article │ │ │ ├── element.js │ │ │ └── queries.js │ │ ├── artist.js │ │ ├── artist │ │ │ ├── element.js │ │ │ └── queries.js │ │ ├── artwork.js │ │ ├── artwork │ │ │ ├── element.js │ │ │ └── queries.js │ │ ├── main-menu.js │ │ ├── serendipity.js │ │ ├── serendipity │ │ │ └── queries.js │ │ ├── settings.js │ │ ├── shows.js │ │ └── shows │ │ │ ├── element.js │ │ │ └── queries.js │ ├── facebook_examples.js │ ├── message-parser.js │ ├── postback-manager.js │ ├── types.js │ ├── user-setup.js │ └── webhook.js ├── db │ └── mongo.js ├── facebook │ ├── api.js │ ├── facebook-comms.js │ ├── facebook-user-login.js │ └── types.js ├── globals.js ├── mitosis.js └── scripts │ └── hourly.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-3" 5 | ], 6 | "plugins": [ 7 | "transform-flow-strip-types", 8 | "syntax-async-functions", 9 | "transform-regenerator" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "jest": true 5 | }, 6 | "parser": "babel-eslint", 7 | "extends": "standard", 8 | "plugins": [ 9 | "standard", 10 | "promise", 11 | "flowtype", 12 | "import" 13 | ], 14 | "presets": ["es2015"], 15 | "rules": { 16 | // Yeah, I know, but I was surprised to find that pretty clear, readable code 17 | // would get caught by it. 18 | "brace-style": [0], 19 | "prefer-const": ["error", { 20 | "destructuring": "any", 21 | "ignoreReadBeforeAssign": false 22 | }], 23 | "space-before-function-paren": ["error", "never"], 24 | "quotes": [1, "double", "avoid-escape"], 25 | "flowtype/define-flow-type": 1, 26 | "flowtype/require-parameter-type": [1, { "excludeArrowFunctions": true }], 27 | "flowtype/require-return-type": [ 0,"always", { "annotateUndefined": "never" } ], 28 | "flowtype/space-after-type-colon": [ 1, "always" ], 29 | "flowtype/space-before-type-colon": [ 1, "never" ], 30 | "flowtype/type-id-match": [ 0, "^([A-Z][a-z0-9]+)+Type$" ], 31 | "flowtype/use-flow-type": 1, 32 | "flowtype/valid-syntax": 1 33 | }, 34 | "ignore": [ 35 | "distribution", 36 | "app.js", 37 | "flow-typed" 38 | ], 39 | "settings": { 40 | "flowtype": { 41 | "onlyFilesWithFlowAnnotation": false 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | distribution 3 | app.js 4 | 5 | [include] 6 | 7 | [libs] 8 | flow-typed/ 9 | 10 | [options] 11 | unsafe.enable_getters_and_setters=true 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | pids 4 | node_modules 5 | .npm 6 | *.dat 7 | config/default.json 8 | distribution -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "javascript.validate.enable": false, 3 | "files.exclude": { 4 | "**/.git": true, 5 | "**/.svn": true, 6 | "**/.hg": true, 7 | "**/.DS_Store": true, 8 | "distribution/": true 9 | }, 10 | "search.exclude": { 11 | "**/node_modules": true, 12 | "distribution/": true 13 | } 14 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Orta Therox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node distribution/mitosis.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Mitosis 2 | 3 | A Facebook Messenger bot for Artsy. 4 | 5 | Allows you to explore Artworks, Artists and Articles in a pretty deep way by just clicking around. 6 | 7 | ![](images/screenshot.png) 8 | 9 | Currently it's being hosted on my personal heroku, and with my own personal facebook app "Ortsy-art". Will be migrated into a real staging environment at some point. 10 | 11 | ### App Structure 12 | 13 | This is an Express app (see [mitosis.js](source/mitosis.js)), that relies on [Flow](https://flowtype.org) to interact with the Facebook API. It interacts with Facebook via a single `"/webhook"` response, and a single post API to Facebook. 14 | 15 | If this style of JavaScript is new to you, consult the [Danger JS guide](https://github.com/danger/danger-js/blob/master/docs/js_glossary.md). 16 | 17 | The app splits into a few sections: `bot`, `db` and `facebook`. The [bot](source/bots) is comprised of some core classes, then contexts. Each context is a section of the artsy browsing experience ( e.g. artist, artwork, gene etc.) 18 | 19 | ### Want to test it? 20 | 21 | You need to have Orta add your Facebook account to the testers page ([link](https://developers.facebook.com/apps/1084256578353668/roles/)). Then go to the [heroku page](https://orta-artsy-facebook-bot.herokuapp.com) and click "Message Us". 22 | 23 | The easiest way to start is to say "trending artists", then start interacting via the buttons that are provided. 24 | 25 | If you want to see all off the potential elements we can use, say any of: `"gif", "audio", "video", "image", "file", "button", "generic", "receipt", "quick reply", "read receipt", "typing on", "typing off", "account linking"`. 26 | 27 | ### Read More 28 | 29 | * [Messenger API docs](https://developers.facebook.com/docs/messenger-platform) 30 | * [Messenget Platform Guidelines](https://developers.facebook.com/docs/messenger-platform/guidelines) 31 | 32 | ### What is the focus? 33 | 34 | - [x] Providing another way to subscribe to Artsy Articles 35 | - [x] Offer a way to dive into some of our data 36 | 37 | ### Setting up for Dev 38 | 39 | Clone the repo, run `yarn install` 40 | 41 | ```sh 42 | git clone https://github.com/artsy/Mitosis.git 43 | cd mitosis 44 | yarn install 45 | ``` 46 | 47 | This is still a bit rough around the edges for contributors. As you cannot easily use the Facebook API to send you webhook notifications to you. However, to run a copy of the server, create a copy of `config.default.json.example` and make it `config.default`. 48 | 49 | ### TODO: 50 | 51 | * Allow you to connect to your Artsy account 52 | * Offer a way to get occasional notifications to trigger exploration 53 | * Think about what chat-like interfaces feel like in practice 54 | * Xapp tokens will need a way to consider themselves out of date, and get new ones 55 | 56 | This is based on the Facebook Sample app, then I added flow, and started making it [a real project](https://github.com/fbsamples/messenger-platform-samples). 57 | -------------------------------------------------------------------------------- /config/default.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "appSecret": "", 3 | "pageAccessToken": "", 4 | "validationToken": "", 5 | "artsyAPIClient": "", 6 | "artsyAPISecret": "", 7 | "serverURL": "", 8 | "gravityURL": "", 9 | "metaphysicsURL": "", 10 | "webURL": "", 11 | "databaseURL": "localhost:27017" 12 | } 13 | -------------------------------------------------------------------------------- /docs/example_flow.md: -------------------------------------------------------------------------------- 1 | ## Transcript: 2 | 3 | #### Scheduled updates - artworks 4 | 5 | Artsy: 6 | Welcome to your {weekly, bi-weekly} update. 7 | We thought you'd like to see some works by {Banksy} this week 8 | 9 | [ ] [ ] [ ] 10 | <3 work, view on Artsy, more about artist 11 | 12 | User: 13 | <3 work 14 | 15 | Artsy: 16 | Favourited {Work name} 17 | (Favourite Artist) (Show more in [Top Gene]) (Open Artist) 18 | 19 | User: 20 | Open Artist 21 | 22 | Artsy: 23 | Banksy, 1980- 24 | {Description} 25 | {x works} 26 | [] [] [] 27 | (Favourite Artist) (Open Artist) 28 | 29 | User: 30 | - exits chat 31 | 32 | #### Scheduled updates - Editorial 33 | 34 | Artsy: 35 | Welcome to your {weekly, bi-weekly} update. 36 | We thought you'd be interested in these articles from {authors} 37 | 38 | [] [] [] 39 | Open Article, 40 | 41 | 42 | User: 43 | click 44 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-cli_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: b50eee5eab16a387bfaa974957c9898f 2 | // flow-typed version: <>/babel-cli_v^6.18.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-cli' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-cli' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-cli/bin/babel-doctor' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-cli/bin/babel-external-helpers' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'babel-cli/bin/babel-node' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'babel-cli/bin/babel' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'babel-cli/lib/_babel-node' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'babel-cli/lib/babel-external-helpers' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'babel-cli/lib/babel-node' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'babel-cli/lib/babel/dir' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'babel-cli/lib/babel/file' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'babel-cli/lib/babel/index' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'babel-cli/lib/babel/util' { 66 | declare module.exports: any; 67 | } 68 | 69 | // Filename aliases 70 | declare module 'babel-cli/bin/babel-doctor.js' { 71 | declare module.exports: $Exports<'babel-cli/bin/babel-doctor'>; 72 | } 73 | declare module 'babel-cli/bin/babel-external-helpers.js' { 74 | declare module.exports: $Exports<'babel-cli/bin/babel-external-helpers'>; 75 | } 76 | declare module 'babel-cli/bin/babel-node.js' { 77 | declare module.exports: $Exports<'babel-cli/bin/babel-node'>; 78 | } 79 | declare module 'babel-cli/bin/babel.js' { 80 | declare module.exports: $Exports<'babel-cli/bin/babel'>; 81 | } 82 | declare module 'babel-cli/index' { 83 | declare module.exports: $Exports<'babel-cli'>; 84 | } 85 | declare module 'babel-cli/index.js' { 86 | declare module.exports: $Exports<'babel-cli'>; 87 | } 88 | declare module 'babel-cli/lib/_babel-node.js' { 89 | declare module.exports: $Exports<'babel-cli/lib/_babel-node'>; 90 | } 91 | declare module 'babel-cli/lib/babel-external-helpers.js' { 92 | declare module.exports: $Exports<'babel-cli/lib/babel-external-helpers'>; 93 | } 94 | declare module 'babel-cli/lib/babel-node.js' { 95 | declare module.exports: $Exports<'babel-cli/lib/babel-node'>; 96 | } 97 | declare module 'babel-cli/lib/babel/dir.js' { 98 | declare module.exports: $Exports<'babel-cli/lib/babel/dir'>; 99 | } 100 | declare module 'babel-cli/lib/babel/file.js' { 101 | declare module.exports: $Exports<'babel-cli/lib/babel/file'>; 102 | } 103 | declare module 'babel-cli/lib/babel/index.js' { 104 | declare module.exports: $Exports<'babel-cli/lib/babel/index'>; 105 | } 106 | declare module 'babel-cli/lib/babel/util.js' { 107 | declare module.exports: $Exports<'babel-cli/lib/babel/util'>; 108 | } 109 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-eslint_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 962499b38caee12adf7f3144ec7059d9 2 | // flow-typed version: <>/babel-eslint_v^7.1.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-eslint' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-eslint' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-eslint/babylon-to-espree/attachComments' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-eslint/babylon-to-espree/convertTemplateType' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'babel-eslint/babylon-to-espree/index' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'babel-eslint/babylon-to-espree/toAST' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'babel-eslint/babylon-to-espree/toToken' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'babel-eslint/babylon-to-espree/toTokens' { 46 | declare module.exports: any; 47 | } 48 | 49 | // Filename aliases 50 | declare module 'babel-eslint/babylon-to-espree/attachComments.js' { 51 | declare module.exports: $Exports<'babel-eslint/babylon-to-espree/attachComments'>; 52 | } 53 | declare module 'babel-eslint/babylon-to-espree/convertTemplateType.js' { 54 | declare module.exports: $Exports<'babel-eslint/babylon-to-espree/convertTemplateType'>; 55 | } 56 | declare module 'babel-eslint/babylon-to-espree/index.js' { 57 | declare module.exports: $Exports<'babel-eslint/babylon-to-espree/index'>; 58 | } 59 | declare module 'babel-eslint/babylon-to-espree/toAST.js' { 60 | declare module.exports: $Exports<'babel-eslint/babylon-to-espree/toAST'>; 61 | } 62 | declare module 'babel-eslint/babylon-to-espree/toToken.js' { 63 | declare module.exports: $Exports<'babel-eslint/babylon-to-espree/toToken'>; 64 | } 65 | declare module 'babel-eslint/babylon-to-espree/toTokens.js' { 66 | declare module.exports: $Exports<'babel-eslint/babylon-to-espree/toTokens'>; 67 | } 68 | declare module 'babel-eslint/index' { 69 | declare module.exports: $Exports<'babel-eslint'>; 70 | } 71 | declare module 'babel-eslint/index.js' { 72 | declare module.exports: $Exports<'babel-eslint'>; 73 | } 74 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-plugin-syntax-async-functions_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 95c2dfd7e207f794863753b40bcea86e 2 | // flow-typed version: <>/babel-plugin-syntax-async-functions_v^6.13.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-plugin-syntax-async-functions' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-plugin-syntax-async-functions' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-plugin-syntax-async-functions/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'babel-plugin-syntax-async-functions/lib/index.js' { 31 | declare module.exports: $Exports<'babel-plugin-syntax-async-functions/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 890b1caa61215a15bbae22bf618b1aa1 2 | // flow-typed version: <>/babel-plugin-transform-flow-strip-types_v^6.18.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-plugin-transform-flow-strip-types' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-plugin-transform-flow-strip-types' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-plugin-transform-flow-strip-types/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'babel-plugin-transform-flow-strip-types/lib/index.js' { 31 | declare module.exports: $Exports<'babel-plugin-transform-flow-strip-types/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-plugin-transform-regenerator_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: ac5ba541505939827a0087290f2e3211 2 | // flow-typed version: <>/babel-plugin-transform-regenerator_v^6.16.1/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-plugin-transform-regenerator' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-plugin-transform-regenerator' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-plugin-transform-regenerator/lib/emit' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-plugin-transform-regenerator/lib/hoist' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'babel-plugin-transform-regenerator/lib/index' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'babel-plugin-transform-regenerator/lib/leap' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'babel-plugin-transform-regenerator/lib/meta' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'babel-plugin-transform-regenerator/lib/util' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'babel-plugin-transform-regenerator/lib/visit' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'babel-plugin-transform-regenerator/src/emit' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'babel-plugin-transform-regenerator/src/hoist' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'babel-plugin-transform-regenerator/src/index' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'babel-plugin-transform-regenerator/src/leap' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'babel-plugin-transform-regenerator/src/meta' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'babel-plugin-transform-regenerator/src/util' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'babel-plugin-transform-regenerator/src/visit' { 78 | declare module.exports: any; 79 | } 80 | 81 | // Filename aliases 82 | declare module 'babel-plugin-transform-regenerator/lib/emit.js' { 83 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/emit'>; 84 | } 85 | declare module 'babel-plugin-transform-regenerator/lib/hoist.js' { 86 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/hoist'>; 87 | } 88 | declare module 'babel-plugin-transform-regenerator/lib/index.js' { 89 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/index'>; 90 | } 91 | declare module 'babel-plugin-transform-regenerator/lib/leap.js' { 92 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/leap'>; 93 | } 94 | declare module 'babel-plugin-transform-regenerator/lib/meta.js' { 95 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/meta'>; 96 | } 97 | declare module 'babel-plugin-transform-regenerator/lib/util.js' { 98 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/util'>; 99 | } 100 | declare module 'babel-plugin-transform-regenerator/lib/visit.js' { 101 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/lib/visit'>; 102 | } 103 | declare module 'babel-plugin-transform-regenerator/src/emit.js' { 104 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/emit'>; 105 | } 106 | declare module 'babel-plugin-transform-regenerator/src/hoist.js' { 107 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/hoist'>; 108 | } 109 | declare module 'babel-plugin-transform-regenerator/src/index.js' { 110 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/index'>; 111 | } 112 | declare module 'babel-plugin-transform-regenerator/src/leap.js' { 113 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/leap'>; 114 | } 115 | declare module 'babel-plugin-transform-regenerator/src/meta.js' { 116 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/meta'>; 117 | } 118 | declare module 'babel-plugin-transform-regenerator/src/util.js' { 119 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/util'>; 120 | } 121 | declare module 'babel-plugin-transform-regenerator/src/visit.js' { 122 | declare module.exports: $Exports<'babel-plugin-transform-regenerator/src/visit'>; 123 | } 124 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-polyfill_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6c8014ea6a3e5a9ed6c374765331f7d3 2 | // flow-typed version: <>/babel-polyfill_v^6.16.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-polyfill' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-polyfill' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-polyfill/browser' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-polyfill/dist/polyfill' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'babel-polyfill/dist/polyfill.min' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'babel-polyfill/lib/index' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'babel-polyfill/scripts/postpublish' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'babel-polyfill/scripts/prepublish' { 46 | declare module.exports: any; 47 | } 48 | 49 | // Filename aliases 50 | declare module 'babel-polyfill/browser.js' { 51 | declare module.exports: $Exports<'babel-polyfill/browser'>; 52 | } 53 | declare module 'babel-polyfill/dist/polyfill.js' { 54 | declare module.exports: $Exports<'babel-polyfill/dist/polyfill'>; 55 | } 56 | declare module 'babel-polyfill/dist/polyfill.min.js' { 57 | declare module.exports: $Exports<'babel-polyfill/dist/polyfill.min'>; 58 | } 59 | declare module 'babel-polyfill/lib/index.js' { 60 | declare module.exports: $Exports<'babel-polyfill/lib/index'>; 61 | } 62 | declare module 'babel-polyfill/scripts/postpublish.js' { 63 | declare module.exports: $Exports<'babel-polyfill/scripts/postpublish'>; 64 | } 65 | declare module 'babel-polyfill/scripts/prepublish.js' { 66 | declare module.exports: $Exports<'babel-polyfill/scripts/prepublish'>; 67 | } 68 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-preset-es2015_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 7173947441ea0ca6b32f3adb491ec7bf 2 | // flow-typed version: <>/babel-preset-es2015_v^6.18.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-preset-es2015' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-preset-es2015' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-preset-es2015/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'babel-preset-es2015/lib/index.js' { 31 | declare module.exports: $Exports<'babel-preset-es2015/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-preset-stage-3_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 56517ed9f6af4cf01f4e622a879d9a2d 2 | // flow-typed version: <>/babel-preset-stage-3_v^6.17.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-preset-stage-3' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-preset-stage-3' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-preset-stage-3/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'babel-preset-stage-3/lib/index.js' { 31 | declare module.exports: $Exports<'babel-preset-stage-3/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-watch_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 4716ad577d3eef1a4bbdf315abb9af21 2 | // flow-typed version: <>/babel-watch_v^2.0.3/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-watch' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-watch' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-watch/babel-watch' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-watch/runner' { 30 | declare module.exports: any; 31 | } 32 | 33 | // Filename aliases 34 | declare module 'babel-watch/babel-watch.js' { 35 | declare module.exports: $Exports<'babel-watch/babel-watch'>; 36 | } 37 | declare module 'babel-watch/runner.js' { 38 | declare module.exports: $Exports<'babel-watch/runner'>; 39 | } 40 | -------------------------------------------------------------------------------- /flow-typed/npm/body-parser_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 4c82e6b0d9830f84def229ca8868def1 2 | // flow-typed version: <>/body-parser_v^1.15.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'body-parser' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'body-parser' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'body-parser/lib/read' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'body-parser/lib/types/json' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'body-parser/lib/types/raw' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'body-parser/lib/types/text' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'body-parser/lib/types/urlencoded' { 42 | declare module.exports: any; 43 | } 44 | 45 | // Filename aliases 46 | declare module 'body-parser/index' { 47 | declare module.exports: $Exports<'body-parser'>; 48 | } 49 | declare module 'body-parser/index.js' { 50 | declare module.exports: $Exports<'body-parser'>; 51 | } 52 | declare module 'body-parser/lib/read.js' { 53 | declare module.exports: $Exports<'body-parser/lib/read'>; 54 | } 55 | declare module 'body-parser/lib/types/json.js' { 56 | declare module.exports: $Exports<'body-parser/lib/types/json'>; 57 | } 58 | declare module 'body-parser/lib/types/raw.js' { 59 | declare module.exports: $Exports<'body-parser/lib/types/raw'>; 60 | } 61 | declare module 'body-parser/lib/types/text.js' { 62 | declare module.exports: $Exports<'body-parser/lib/types/text'>; 63 | } 64 | declare module 'body-parser/lib/types/urlencoded.js' { 65 | declare module.exports: $Exports<'body-parser/lib/types/urlencoded'>; 66 | } 67 | -------------------------------------------------------------------------------- /flow-typed/npm/config_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: fb6db75c26f11c9f31131f43855ef277 2 | // flow-typed version: <>/config_v^1.20.4/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'config' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'config' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'config/defer' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'config/lib/config' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'config/raw' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'config/tools/contributors' { 38 | declare module.exports: any; 39 | } 40 | 41 | // Filename aliases 42 | declare module 'config/defer.js' { 43 | declare module.exports: $Exports<'config/defer'>; 44 | } 45 | declare module 'config/lib/config.js' { 46 | declare module.exports: $Exports<'config/lib/config'>; 47 | } 48 | declare module 'config/raw.js' { 49 | declare module.exports: $Exports<'config/raw'>; 50 | } 51 | declare module 'config/tools/contributors.js' { 52 | declare module.exports: $Exports<'config/tools/contributors'>; 53 | } 54 | -------------------------------------------------------------------------------- /flow-typed/npm/ejs_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6916eb14bf8e7208b37b3235eb914531 2 | // flow-typed version: <>/ejs_v^2.4.2/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'ejs' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'ejs' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'ejs/ejs' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'ejs/ejs.min' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'ejs/lib/ejs' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'ejs/lib/utils' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'ejs/test/ejs' { 42 | declare module.exports: any; 43 | } 44 | 45 | // Filename aliases 46 | declare module 'ejs/ejs.js' { 47 | declare module.exports: $Exports<'ejs/ejs'>; 48 | } 49 | declare module 'ejs/ejs.min.js' { 50 | declare module.exports: $Exports<'ejs/ejs.min'>; 51 | } 52 | declare module 'ejs/lib/ejs.js' { 53 | declare module.exports: $Exports<'ejs/lib/ejs'>; 54 | } 55 | declare module 'ejs/lib/utils.js' { 56 | declare module.exports: $Exports<'ejs/lib/utils'>; 57 | } 58 | declare module 'ejs/test/ejs.js' { 59 | declare module.exports: $Exports<'ejs/test/ejs'>; 60 | } 61 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-config-standard_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 47a2367918ed83f2d934e5cb46e9cffb 2 | // flow-typed version: <>/eslint-config-standard_v^6.2.1/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-config-standard' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-config-standard' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-config-standard/test/basic' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-config-standard/test/validate-config' { 30 | declare module.exports: any; 31 | } 32 | 33 | // Filename aliases 34 | declare module 'eslint-config-standard/index' { 35 | declare module.exports: $Exports<'eslint-config-standard'>; 36 | } 37 | declare module 'eslint-config-standard/index.js' { 38 | declare module.exports: $Exports<'eslint-config-standard'>; 39 | } 40 | declare module 'eslint-config-standard/test/basic.js' { 41 | declare module.exports: $Exports<'eslint-config-standard/test/basic'>; 42 | } 43 | declare module 'eslint-config-standard/test/validate-config.js' { 44 | declare module.exports: $Exports<'eslint-config-standard/test/validate-config'>; 45 | } 46 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 574cb2a6f2cb3590ce449f852f404bf8 2 | // flow-typed version: <>/eslint-plugin-flowtype_v^2.25.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-plugin-flowtype' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-plugin-flowtype' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-plugin-flowtype/bin/readmeAssertions' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-plugin-flowtype/dist/index' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'eslint-plugin-flowtype/dist/rules/booleanStyle' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'eslint-plugin-flowtype/dist/rules/defineFlowType' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'eslint-plugin-flowtype/dist/rules/delimiterDangle' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'eslint-plugin-flowtype/dist/rules/genericSpacing' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'eslint-plugin-flowtype/dist/rules/noDupeKeys' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'eslint-plugin-flowtype/dist/rules/noWeakTypes' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'eslint-plugin-flowtype/dist/rules/requireParameterType' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'eslint-plugin-flowtype/dist/rules/requireReturnType' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'eslint-plugin-flowtype/dist/rules/semi' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'eslint-plugin-flowtype/dist/rules/sortKeys' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'eslint-plugin-flowtype/dist/rules/spaceAfterTypeColon' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeGenericBracket' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeTypeColon' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateFunctions' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeIndexer' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeProperty' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateReturnType' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeCastExpression' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter' { 122 | declare module.exports: any; 123 | } 124 | 125 | declare module 'eslint-plugin-flowtype/dist/rules/typeIdMatch' { 126 | declare module.exports: any; 127 | } 128 | 129 | declare module 'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing' { 130 | declare module.exports: any; 131 | } 132 | 133 | declare module 'eslint-plugin-flowtype/dist/rules/useFlowType' { 134 | declare module.exports: any; 135 | } 136 | 137 | declare module 'eslint-plugin-flowtype/dist/rules/validSyntax' { 138 | declare module.exports: any; 139 | } 140 | 141 | declare module 'eslint-plugin-flowtype/dist/utilities/getParameterName' { 142 | declare module.exports: any; 143 | } 144 | 145 | declare module 'eslint-plugin-flowtype/dist/utilities/getTokenAfterParens' { 146 | declare module.exports: any; 147 | } 148 | 149 | declare module 'eslint-plugin-flowtype/dist/utilities/getTokenBeforeParens' { 150 | declare module.exports: any; 151 | } 152 | 153 | declare module 'eslint-plugin-flowtype/dist/utilities/index' { 154 | declare module.exports: any; 155 | } 156 | 157 | declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFile' { 158 | declare module.exports: any; 159 | } 160 | 161 | declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFileAnnotation' { 162 | declare module.exports: any; 163 | } 164 | 165 | declare module 'eslint-plugin-flowtype/dist/utilities/iterateFunctionNodes' { 166 | declare module.exports: any; 167 | } 168 | 169 | declare module 'eslint-plugin-flowtype/dist/utilities/quoteName' { 170 | declare module.exports: any; 171 | } 172 | 173 | declare module 'eslint-plugin-flowtype/dist/utilities/spacingFixers' { 174 | declare module.exports: any; 175 | } 176 | 177 | // Filename aliases 178 | declare module 'eslint-plugin-flowtype/bin/readmeAssertions.js' { 179 | declare module.exports: $Exports<'eslint-plugin-flowtype/bin/readmeAssertions'>; 180 | } 181 | declare module 'eslint-plugin-flowtype/dist/index.js' { 182 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/index'>; 183 | } 184 | declare module 'eslint-plugin-flowtype/dist/rules/booleanStyle.js' { 185 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/booleanStyle'>; 186 | } 187 | declare module 'eslint-plugin-flowtype/dist/rules/defineFlowType.js' { 188 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/defineFlowType'>; 189 | } 190 | declare module 'eslint-plugin-flowtype/dist/rules/delimiterDangle.js' { 191 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/delimiterDangle'>; 192 | } 193 | declare module 'eslint-plugin-flowtype/dist/rules/genericSpacing.js' { 194 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/genericSpacing'>; 195 | } 196 | declare module 'eslint-plugin-flowtype/dist/rules/noDupeKeys.js' { 197 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noDupeKeys'>; 198 | } 199 | declare module 'eslint-plugin-flowtype/dist/rules/noWeakTypes.js' { 200 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noWeakTypes'>; 201 | } 202 | declare module 'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter.js' { 203 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter'>; 204 | } 205 | declare module 'eslint-plugin-flowtype/dist/rules/requireParameterType.js' { 206 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireParameterType'>; 207 | } 208 | declare module 'eslint-plugin-flowtype/dist/rules/requireReturnType.js' { 209 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireReturnType'>; 210 | } 211 | declare module 'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation.js' { 212 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation'>; 213 | } 214 | declare module 'eslint-plugin-flowtype/dist/rules/semi.js' { 215 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/semi'>; 216 | } 217 | declare module 'eslint-plugin-flowtype/dist/rules/sortKeys.js' { 218 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/sortKeys'>; 219 | } 220 | declare module 'eslint-plugin-flowtype/dist/rules/spaceAfterTypeColon.js' { 221 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/spaceAfterTypeColon'>; 222 | } 223 | declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeGenericBracket.js' { 224 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/spaceBeforeGenericBracket'>; 225 | } 226 | declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeTypeColon.js' { 227 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/spaceBeforeTypeColon'>; 228 | } 229 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateFunctions.js' { 230 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateFunctions'>; 231 | } 232 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeIndexer.js' { 233 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeIndexer'>; 234 | } 235 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeProperty.js' { 236 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeProperty'>; 237 | } 238 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateReturnType.js' { 239 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateReturnType'>; 240 | } 241 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeCastExpression.js' { 242 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeCastExpression'>; 243 | } 244 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical.js' { 245 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical'>; 246 | } 247 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index.js' { 248 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index'>; 249 | } 250 | declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter.js' { 251 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter'>; 252 | } 253 | declare module 'eslint-plugin-flowtype/dist/rules/typeIdMatch.js' { 254 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeIdMatch'>; 255 | } 256 | declare module 'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing.js' { 257 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing'>; 258 | } 259 | declare module 'eslint-plugin-flowtype/dist/rules/useFlowType.js' { 260 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/useFlowType'>; 261 | } 262 | declare module 'eslint-plugin-flowtype/dist/rules/validSyntax.js' { 263 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/validSyntax'>; 264 | } 265 | declare module 'eslint-plugin-flowtype/dist/utilities/getParameterName.js' { 266 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/getParameterName'>; 267 | } 268 | declare module 'eslint-plugin-flowtype/dist/utilities/getTokenAfterParens.js' { 269 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/getTokenAfterParens'>; 270 | } 271 | declare module 'eslint-plugin-flowtype/dist/utilities/getTokenBeforeParens.js' { 272 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/getTokenBeforeParens'>; 273 | } 274 | declare module 'eslint-plugin-flowtype/dist/utilities/index.js' { 275 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/index'>; 276 | } 277 | declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFile.js' { 278 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/isFlowFile'>; 279 | } 280 | declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFileAnnotation.js' { 281 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/isFlowFileAnnotation'>; 282 | } 283 | declare module 'eslint-plugin-flowtype/dist/utilities/iterateFunctionNodes.js' { 284 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/iterateFunctionNodes'>; 285 | } 286 | declare module 'eslint-plugin-flowtype/dist/utilities/quoteName.js' { 287 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/quoteName'>; 288 | } 289 | declare module 'eslint-plugin-flowtype/dist/utilities/spacingFixers.js' { 290 | declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/spacingFixers'>; 291 | } 292 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-plugin-import_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: ef91391461dae957858ac24358e219e4 2 | // flow-typed version: <>/eslint-plugin-import_v^2.2.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-plugin-import' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-plugin-import' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-plugin-import/config/electron' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-plugin-import/config/errors' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'eslint-plugin-import/config/react-native' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'eslint-plugin-import/config/react' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'eslint-plugin-import/config/recommended' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'eslint-plugin-import/config/stage-0' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'eslint-plugin-import/config/warnings' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'eslint-plugin-import/lib/core/importType' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'eslint-plugin-import/lib/core/staticRequire' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'eslint-plugin-import/lib/ExportMap' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'eslint-plugin-import/lib/importDeclaration' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'eslint-plugin-import/lib/index' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'eslint-plugin-import/lib/rules/default' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'eslint-plugin-import/lib/rules/export' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'eslint-plugin-import/lib/rules/extensions' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'eslint-plugin-import/lib/rules/first' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'eslint-plugin-import/lib/rules/imports-first' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'eslint-plugin-import/lib/rules/max-dependencies' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'eslint-plugin-import/lib/rules/named' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'eslint-plugin-import/lib/rules/namespace' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'eslint-plugin-import/lib/rules/newline-after-import' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'eslint-plugin-import/lib/rules/no-absolute-path' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'eslint-plugin-import/lib/rules/no-amd' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'eslint-plugin-import/lib/rules/no-commonjs' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'eslint-plugin-import/lib/rules/no-deprecated' { 122 | declare module.exports: any; 123 | } 124 | 125 | declare module 'eslint-plugin-import/lib/rules/no-duplicates' { 126 | declare module.exports: any; 127 | } 128 | 129 | declare module 'eslint-plugin-import/lib/rules/no-dynamic-require' { 130 | declare module.exports: any; 131 | } 132 | 133 | declare module 'eslint-plugin-import/lib/rules/no-extraneous-dependencies' { 134 | declare module.exports: any; 135 | } 136 | 137 | declare module 'eslint-plugin-import/lib/rules/no-internal-modules' { 138 | declare module.exports: any; 139 | } 140 | 141 | declare module 'eslint-plugin-import/lib/rules/no-mutable-exports' { 142 | declare module.exports: any; 143 | } 144 | 145 | declare module 'eslint-plugin-import/lib/rules/no-named-as-default-member' { 146 | declare module.exports: any; 147 | } 148 | 149 | declare module 'eslint-plugin-import/lib/rules/no-named-as-default' { 150 | declare module.exports: any; 151 | } 152 | 153 | declare module 'eslint-plugin-import/lib/rules/no-named-default' { 154 | declare module.exports: any; 155 | } 156 | 157 | declare module 'eslint-plugin-import/lib/rules/no-namespace' { 158 | declare module.exports: any; 159 | } 160 | 161 | declare module 'eslint-plugin-import/lib/rules/no-nodejs-modules' { 162 | declare module.exports: any; 163 | } 164 | 165 | declare module 'eslint-plugin-import/lib/rules/no-restricted-paths' { 166 | declare module.exports: any; 167 | } 168 | 169 | declare module 'eslint-plugin-import/lib/rules/no-unassigned-import' { 170 | declare module.exports: any; 171 | } 172 | 173 | declare module 'eslint-plugin-import/lib/rules/no-unresolved' { 174 | declare module.exports: any; 175 | } 176 | 177 | declare module 'eslint-plugin-import/lib/rules/no-webpack-loader-syntax' { 178 | declare module.exports: any; 179 | } 180 | 181 | declare module 'eslint-plugin-import/lib/rules/order' { 182 | declare module.exports: any; 183 | } 184 | 185 | declare module 'eslint-plugin-import/lib/rules/prefer-default-export' { 186 | declare module.exports: any; 187 | } 188 | 189 | declare module 'eslint-plugin-import/lib/rules/unambiguous' { 190 | declare module.exports: any; 191 | } 192 | 193 | declare module 'eslint-plugin-import/memo-parser/index' { 194 | declare module.exports: any; 195 | } 196 | 197 | // Filename aliases 198 | declare module 'eslint-plugin-import/config/electron.js' { 199 | declare module.exports: $Exports<'eslint-plugin-import/config/electron'>; 200 | } 201 | declare module 'eslint-plugin-import/config/errors.js' { 202 | declare module.exports: $Exports<'eslint-plugin-import/config/errors'>; 203 | } 204 | declare module 'eslint-plugin-import/config/react-native.js' { 205 | declare module.exports: $Exports<'eslint-plugin-import/config/react-native'>; 206 | } 207 | declare module 'eslint-plugin-import/config/react.js' { 208 | declare module.exports: $Exports<'eslint-plugin-import/config/react'>; 209 | } 210 | declare module 'eslint-plugin-import/config/recommended.js' { 211 | declare module.exports: $Exports<'eslint-plugin-import/config/recommended'>; 212 | } 213 | declare module 'eslint-plugin-import/config/stage-0.js' { 214 | declare module.exports: $Exports<'eslint-plugin-import/config/stage-0'>; 215 | } 216 | declare module 'eslint-plugin-import/config/warnings.js' { 217 | declare module.exports: $Exports<'eslint-plugin-import/config/warnings'>; 218 | } 219 | declare module 'eslint-plugin-import/lib/core/importType.js' { 220 | declare module.exports: $Exports<'eslint-plugin-import/lib/core/importType'>; 221 | } 222 | declare module 'eslint-plugin-import/lib/core/staticRequire.js' { 223 | declare module.exports: $Exports<'eslint-plugin-import/lib/core/staticRequire'>; 224 | } 225 | declare module 'eslint-plugin-import/lib/ExportMap.js' { 226 | declare module.exports: $Exports<'eslint-plugin-import/lib/ExportMap'>; 227 | } 228 | declare module 'eslint-plugin-import/lib/importDeclaration.js' { 229 | declare module.exports: $Exports<'eslint-plugin-import/lib/importDeclaration'>; 230 | } 231 | declare module 'eslint-plugin-import/lib/index.js' { 232 | declare module.exports: $Exports<'eslint-plugin-import/lib/index'>; 233 | } 234 | declare module 'eslint-plugin-import/lib/rules/default.js' { 235 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/default'>; 236 | } 237 | declare module 'eslint-plugin-import/lib/rules/export.js' { 238 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/export'>; 239 | } 240 | declare module 'eslint-plugin-import/lib/rules/extensions.js' { 241 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/extensions'>; 242 | } 243 | declare module 'eslint-plugin-import/lib/rules/first.js' { 244 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/first'>; 245 | } 246 | declare module 'eslint-plugin-import/lib/rules/imports-first.js' { 247 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/imports-first'>; 248 | } 249 | declare module 'eslint-plugin-import/lib/rules/max-dependencies.js' { 250 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/max-dependencies'>; 251 | } 252 | declare module 'eslint-plugin-import/lib/rules/named.js' { 253 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/named'>; 254 | } 255 | declare module 'eslint-plugin-import/lib/rules/namespace.js' { 256 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/namespace'>; 257 | } 258 | declare module 'eslint-plugin-import/lib/rules/newline-after-import.js' { 259 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/newline-after-import'>; 260 | } 261 | declare module 'eslint-plugin-import/lib/rules/no-absolute-path.js' { 262 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-absolute-path'>; 263 | } 264 | declare module 'eslint-plugin-import/lib/rules/no-amd.js' { 265 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-amd'>; 266 | } 267 | declare module 'eslint-plugin-import/lib/rules/no-commonjs.js' { 268 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-commonjs'>; 269 | } 270 | declare module 'eslint-plugin-import/lib/rules/no-deprecated.js' { 271 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-deprecated'>; 272 | } 273 | declare module 'eslint-plugin-import/lib/rules/no-duplicates.js' { 274 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-duplicates'>; 275 | } 276 | declare module 'eslint-plugin-import/lib/rules/no-dynamic-require.js' { 277 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-dynamic-require'>; 278 | } 279 | declare module 'eslint-plugin-import/lib/rules/no-extraneous-dependencies.js' { 280 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-extraneous-dependencies'>; 281 | } 282 | declare module 'eslint-plugin-import/lib/rules/no-internal-modules.js' { 283 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-internal-modules'>; 284 | } 285 | declare module 'eslint-plugin-import/lib/rules/no-mutable-exports.js' { 286 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-mutable-exports'>; 287 | } 288 | declare module 'eslint-plugin-import/lib/rules/no-named-as-default-member.js' { 289 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-named-as-default-member'>; 290 | } 291 | declare module 'eslint-plugin-import/lib/rules/no-named-as-default.js' { 292 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-named-as-default'>; 293 | } 294 | declare module 'eslint-plugin-import/lib/rules/no-named-default.js' { 295 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-named-default'>; 296 | } 297 | declare module 'eslint-plugin-import/lib/rules/no-namespace.js' { 298 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-namespace'>; 299 | } 300 | declare module 'eslint-plugin-import/lib/rules/no-nodejs-modules.js' { 301 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-nodejs-modules'>; 302 | } 303 | declare module 'eslint-plugin-import/lib/rules/no-restricted-paths.js' { 304 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-restricted-paths'>; 305 | } 306 | declare module 'eslint-plugin-import/lib/rules/no-unassigned-import.js' { 307 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-unassigned-import'>; 308 | } 309 | declare module 'eslint-plugin-import/lib/rules/no-unresolved.js' { 310 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-unresolved'>; 311 | } 312 | declare module 'eslint-plugin-import/lib/rules/no-webpack-loader-syntax.js' { 313 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-webpack-loader-syntax'>; 314 | } 315 | declare module 'eslint-plugin-import/lib/rules/order.js' { 316 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/order'>; 317 | } 318 | declare module 'eslint-plugin-import/lib/rules/prefer-default-export.js' { 319 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/prefer-default-export'>; 320 | } 321 | declare module 'eslint-plugin-import/lib/rules/unambiguous.js' { 322 | declare module.exports: $Exports<'eslint-plugin-import/lib/rules/unambiguous'>; 323 | } 324 | declare module 'eslint-plugin-import/memo-parser/index.js' { 325 | declare module.exports: $Exports<'eslint-plugin-import/memo-parser/index'>; 326 | } 327 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-plugin-promise_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 466fb8239d5522711dff1e52e8b0855e 2 | // flow-typed version: <>/eslint-plugin-promise_v^3.3.1/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-plugin-promise' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-plugin-promise' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-plugin-promise/rules/always-return' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-plugin-promise/rules/catch-or-return' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'eslint-plugin-promise/rules/lib/is-promise' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'eslint-plugin-promise/rules/no-native' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'eslint-plugin-promise/rules/no-return-wrap' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'eslint-plugin-promise/rules/param-names' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'eslint-plugin-promise/rules/prefer-await-to-callbacks' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'eslint-plugin-promise/rules/prefer-await-to-then' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'eslint-plugin-promise/test/always-return.test' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'eslint-plugin-promise/test/catch-or-return.test' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'eslint-plugin-promise/test/index' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'eslint-plugin-promise/test/no-native' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'eslint-plugin-promise/test/no-return-wrap' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'eslint-plugin-promise/test/param-names.test' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'eslint-plugin-promise/test/prefer-await-to-callbacks' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'eslint-plugin-promise/test/prefer-await-to-then' { 86 | declare module.exports: any; 87 | } 88 | 89 | // Filename aliases 90 | declare module 'eslint-plugin-promise/index' { 91 | declare module.exports: $Exports<'eslint-plugin-promise'>; 92 | } 93 | declare module 'eslint-plugin-promise/index.js' { 94 | declare module.exports: $Exports<'eslint-plugin-promise'>; 95 | } 96 | declare module 'eslint-plugin-promise/rules/always-return.js' { 97 | declare module.exports: $Exports<'eslint-plugin-promise/rules/always-return'>; 98 | } 99 | declare module 'eslint-plugin-promise/rules/catch-or-return.js' { 100 | declare module.exports: $Exports<'eslint-plugin-promise/rules/catch-or-return'>; 101 | } 102 | declare module 'eslint-plugin-promise/rules/lib/is-promise.js' { 103 | declare module.exports: $Exports<'eslint-plugin-promise/rules/lib/is-promise'>; 104 | } 105 | declare module 'eslint-plugin-promise/rules/no-native.js' { 106 | declare module.exports: $Exports<'eslint-plugin-promise/rules/no-native'>; 107 | } 108 | declare module 'eslint-plugin-promise/rules/no-return-wrap.js' { 109 | declare module.exports: $Exports<'eslint-plugin-promise/rules/no-return-wrap'>; 110 | } 111 | declare module 'eslint-plugin-promise/rules/param-names.js' { 112 | declare module.exports: $Exports<'eslint-plugin-promise/rules/param-names'>; 113 | } 114 | declare module 'eslint-plugin-promise/rules/prefer-await-to-callbacks.js' { 115 | declare module.exports: $Exports<'eslint-plugin-promise/rules/prefer-await-to-callbacks'>; 116 | } 117 | declare module 'eslint-plugin-promise/rules/prefer-await-to-then.js' { 118 | declare module.exports: $Exports<'eslint-plugin-promise/rules/prefer-await-to-then'>; 119 | } 120 | declare module 'eslint-plugin-promise/test/always-return.test.js' { 121 | declare module.exports: $Exports<'eslint-plugin-promise/test/always-return.test'>; 122 | } 123 | declare module 'eslint-plugin-promise/test/catch-or-return.test.js' { 124 | declare module.exports: $Exports<'eslint-plugin-promise/test/catch-or-return.test'>; 125 | } 126 | declare module 'eslint-plugin-promise/test/index.js' { 127 | declare module.exports: $Exports<'eslint-plugin-promise/test/index'>; 128 | } 129 | declare module 'eslint-plugin-promise/test/no-native.js' { 130 | declare module.exports: $Exports<'eslint-plugin-promise/test/no-native'>; 131 | } 132 | declare module 'eslint-plugin-promise/test/no-return-wrap.js' { 133 | declare module.exports: $Exports<'eslint-plugin-promise/test/no-return-wrap'>; 134 | } 135 | declare module 'eslint-plugin-promise/test/param-names.test.js' { 136 | declare module.exports: $Exports<'eslint-plugin-promise/test/param-names.test'>; 137 | } 138 | declare module 'eslint-plugin-promise/test/prefer-await-to-callbacks.js' { 139 | declare module.exports: $Exports<'eslint-plugin-promise/test/prefer-await-to-callbacks'>; 140 | } 141 | declare module 'eslint-plugin-promise/test/prefer-await-to-then.js' { 142 | declare module.exports: $Exports<'eslint-plugin-promise/test/prefer-await-to-then'>; 143 | } 144 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-plugin-standard_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 0691a6fa33841f9cf83a934f0afc8c20 2 | // flow-typed version: <>/eslint-plugin-standard_v^2.0.1/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-plugin-standard' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-plugin-standard' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-plugin-standard/rules/array-bracket-even-spacing' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-plugin-standard/rules/computed-property-even-spacing' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'eslint-plugin-standard/rules/object-curly-even-spacing' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'eslint-plugin-standard/tests/array-bracket-even-spacing' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'eslint-plugin-standard/tests/computed-property-even-spacing' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'eslint-plugin-standard/tests/object-curly-even-spacing' { 46 | declare module.exports: any; 47 | } 48 | 49 | // Filename aliases 50 | declare module 'eslint-plugin-standard/index' { 51 | declare module.exports: $Exports<'eslint-plugin-standard'>; 52 | } 53 | declare module 'eslint-plugin-standard/index.js' { 54 | declare module.exports: $Exports<'eslint-plugin-standard'>; 55 | } 56 | declare module 'eslint-plugin-standard/rules/array-bracket-even-spacing.js' { 57 | declare module.exports: $Exports<'eslint-plugin-standard/rules/array-bracket-even-spacing'>; 58 | } 59 | declare module 'eslint-plugin-standard/rules/computed-property-even-spacing.js' { 60 | declare module.exports: $Exports<'eslint-plugin-standard/rules/computed-property-even-spacing'>; 61 | } 62 | declare module 'eslint-plugin-standard/rules/object-curly-even-spacing.js' { 63 | declare module.exports: $Exports<'eslint-plugin-standard/rules/object-curly-even-spacing'>; 64 | } 65 | declare module 'eslint-plugin-standard/tests/array-bracket-even-spacing.js' { 66 | declare module.exports: $Exports<'eslint-plugin-standard/tests/array-bracket-even-spacing'>; 67 | } 68 | declare module 'eslint-plugin-standard/tests/computed-property-even-spacing.js' { 69 | declare module.exports: $Exports<'eslint-plugin-standard/tests/computed-property-even-spacing'>; 70 | } 71 | declare module 'eslint-plugin-standard/tests/object-curly-even-spacing.js' { 72 | declare module.exports: $Exports<'eslint-plugin-standard/tests/object-curly-even-spacing'>; 73 | } 74 | -------------------------------------------------------------------------------- /flow-typed/npm/express_v4.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: c099894613b849a251fe2231073ba9d0 2 | // flow-typed version: d2849ed4c4/express_v4.x.x/flow_>=v0.25.x 3 | 4 | // @flow 5 | import type { Server } from 'http'; 6 | declare module 'express' { 7 | declare type RouterOptions = { 8 | caseSensitive?: boolean, 9 | mergeParams?: boolean, 10 | strict?: boolean 11 | }; 12 | declare class RequestResponseBase { 13 | app: Application; 14 | get(field: string): string | void; 15 | } 16 | declare class Request extends http$IncomingMessage mixins RequestResponseBase { 17 | baseUrl: string; 18 | body: mixed; 19 | cookies: {[cookie: string]: string}; 20 | fresh: boolean; 21 | hostname: boolean; 22 | ip: string; 23 | ips: Array; 24 | method: string; 25 | originalUrl: string; 26 | params: {[param: string]: string}; 27 | path: string; 28 | protocol: 'https' | 'http'; 29 | query: {[name: string]: string}; 30 | route: string; 31 | secure: boolean; 32 | signedCookies: {[signedCookie: string]: string}; 33 | stale: boolean; 34 | subdomains: Array; 35 | xhr: boolean; 36 | accepts(types: string): string | false; 37 | acceptsCharsets(...charsets: Array): string | false; 38 | acceptsEncodings(...encoding: Array): string | false; 39 | acceptsLanguages(...lang: Array): string | false; 40 | header(field: string): string | void; 41 | is(type: string): boolean; 42 | param(name: string, defaultValue?: string): string | void; 43 | } 44 | 45 | 46 | declare type CookieOptions = { 47 | domain?: string, 48 | encode?: (value: string) => string, 49 | expires?: Date, 50 | httpOnly?: boolean, 51 | maxAge?: string, 52 | path?: string, 53 | secure?: boolean, 54 | signed?: boolean 55 | }; 56 | 57 | declare type RenderCallback = (err: Error | null, html?: string) => mixed; 58 | 59 | declare type SendFileOptions = { 60 | maxAge?: number, 61 | root?: string, 62 | lastModified?: boolean, 63 | headers?: {[name: string]: string}, 64 | dotfiles?: 'allow' | 'deny' | 'ignore' 65 | }; 66 | declare class Response extends http$ClientRequest mixins RequestResponseBase { 67 | headersSent: boolean; 68 | locals: {[name: string]: mixed}; 69 | append(field: string, value?: string): this; 70 | attachment(filename?: string): this; 71 | cookie(name: string, value: string, options?: CookieOptions): this; 72 | clearCookie(name: string, options?: CookieOptions): this; 73 | download(path: string, filename?: string, callback?: (err?: ?Error) => void): this; 74 | format(typesObject: {[type: string]: Function}): this; 75 | json(body?: mixed): this; 76 | jsonp(body?: mixed): this; 77 | links(links: {[name: string]: string}): this; 78 | location(path: string): this; 79 | redirect(url: string, ...args: Array): this; 80 | redirect(status: number, url: string, ...args: Array): this; 81 | render(view: string, locals?: {[name: string]: mixed}, callback?: RenderCallback): this; 82 | send(body?: mixed): this; 83 | sendFile(path: string, options?: SendFileOptions, callback?: (err?: ?Error) => mixed): this; 84 | sendStatus(statusCode: number): this; 85 | set(field: string, value?: string): this; 86 | status(statusCode: number): this; 87 | type(type: string): this; 88 | vary(field: string): this; 89 | } 90 | declare type $Response = Response; 91 | declare type $Request = Request; 92 | declare type NextFunction = (err?: ?Error) => mixed; 93 | declare type Middleware = 94 | ((req: Request, res: Response, next: NextFunction) => mixed) | 95 | ((error: ?Error, req : Request, res: Response, next: NextFunction) => mixed); 96 | declare interface RouteMethodType { 97 | (middleware: Middleware): T; 98 | (...middleware: Array): T; 99 | (path: string|RegExp|string[], ...middleware: Array): T; 100 | } 101 | declare interface RouterMethodType { 102 | (middleware: Middleware): T; 103 | (...middleware: Array): T; 104 | (path: string|RegExp|string[], ...middleware: Array): T; 105 | (path: string, router: Router): T; 106 | } 107 | declare class Route { 108 | all: RouteMethodType; 109 | get: RouteMethodType; 110 | post: RouteMethodType; 111 | put: RouteMethodType; 112 | head: RouteMethodType; 113 | delete: RouteMethodType; 114 | options: RouteMethodType; 115 | trace: RouteMethodType; 116 | copy: RouteMethodType; 117 | lock: RouteMethodType; 118 | mkcol: RouteMethodType; 119 | move: RouteMethodType; 120 | purge: RouteMethodType; 121 | propfind: RouteMethodType; 122 | proppatch: RouteMethodType; 123 | unlock: RouteMethodType; 124 | report: RouteMethodType; 125 | mkactivity: RouteMethodType; 126 | checkout: RouteMethodType; 127 | merge: RouteMethodType; 128 | 129 | // @TODO Missing 'm-search' but get flow illegal name error. 130 | 131 | notify: RouteMethodType; 132 | subscribe: RouteMethodType; 133 | unsubscribe: RouteMethodType; 134 | patch: RouteMethodType; 135 | search: RouteMethodType; 136 | connect: RouteMethodType; 137 | } 138 | declare class Router extends Route { 139 | constructor(options?: RouterOptions): void; 140 | 141 | use: RouterMethodType; 142 | 143 | route(path: string): Route; 144 | 145 | static (): Router; 146 | } 147 | 148 | declare function serveStatic(root: string, options?: Object): Middleware; 149 | 150 | declare class Application extends Router mixins events$EventEmitter { 151 | constructor(): void; 152 | locals: {[name: string]: mixed}; 153 | mountpath: string; 154 | listen(port: number, hostname?: string, backlog?: number, callback?: (err?: ?Error) => mixed): Server; 155 | listen(port: number, hostname?: string, callback?: (err?: ?Error) => mixed): Server; 156 | listen(port: number, callback?: (err?: ?Error) => mixed): Server; 157 | listen(path: string, callback?: (err?: ?Error) => mixed): Server; 158 | listen(handle: Object, callback?: (err?: ?Error) => mixed): Server; 159 | disable(name: string): void; 160 | disabled(name: string): boolean; 161 | enable(name: string): void; 162 | enabled(name: string): boolean; 163 | engine(name: string, callback: Function): void; 164 | /** 165 | * Mixed will not be taken as a value option. Issue around using the GET http method name and the get for settings. 166 | */ 167 | // get(name: string): mixed; 168 | set(name: string, value: mixed): mixed; 169 | render(name: string, optionsOrFunction: {[name: string]: mixed}, callback: RenderCallback): void; 170 | } 171 | 172 | declare type $Application = Application; 173 | 174 | declare module.exports: { 175 | (): Application, // If you try to call like a function, it will use this signature 176 | static: serveStatic, // `static` property on the function 177 | Router: typeof Router, // `Router` property on the function 178 | }; 179 | } 180 | -------------------------------------------------------------------------------- /flow-typed/npm/flow-bin_v0.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583 2 | // flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x 3 | 4 | declare module "flow-bin" { 5 | declare module.exports: string; 6 | } 7 | -------------------------------------------------------------------------------- /flow-typed/npm/jest-cli_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 89c823b9bcc7d86696f9d22f8c1e5629 2 | // flow-typed version: <>/jest-cli_v^16.0.2/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'jest-cli' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'jest-cli' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'jest-cli/bin/jest' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'jest-cli/build/cli/args' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'jest-cli/build/cli/getJest' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'jest-cli/build/cli/index' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'jest-cli/build/generateEmptyCoverage' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'jest-cli/build/jest' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'jest-cli/build/lib/BufferedConsole' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'jest-cli/build/lib/formatTestResults' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'jest-cli/build/lib/promisify' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'jest-cli/build/preRunMessage' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'jest-cli/build/reporters/BaseReporter' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'jest-cli/build/reporters/CoverageReporter' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'jest-cli/build/reporters/DefaultReporter' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'jest-cli/build/reporters/getConsoleOutput' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'jest-cli/build/reporters/getResultHeader' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'jest-cli/build/reporters/NotifyReporter' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'jest-cli/build/reporters/Status' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'jest-cli/build/reporters/SummaryReporter' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'jest-cli/build/reporters/utils' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'jest-cli/build/reporters/VerboseReporter' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'jest-cli/build/runTest' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'jest-cli/build/SearchSource' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'jest-cli/build/TestRunner' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'jest-cli/build/TestWatcher' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'jest-cli/build/TestWorker' { 122 | declare module.exports: any; 123 | } 124 | 125 | // Filename aliases 126 | declare module 'jest-cli/bin/jest.js' { 127 | declare module.exports: $Exports<'jest-cli/bin/jest'>; 128 | } 129 | declare module 'jest-cli/build/cli/args.js' { 130 | declare module.exports: $Exports<'jest-cli/build/cli/args'>; 131 | } 132 | declare module 'jest-cli/build/cli/getJest.js' { 133 | declare module.exports: $Exports<'jest-cli/build/cli/getJest'>; 134 | } 135 | declare module 'jest-cli/build/cli/index.js' { 136 | declare module.exports: $Exports<'jest-cli/build/cli/index'>; 137 | } 138 | declare module 'jest-cli/build/generateEmptyCoverage.js' { 139 | declare module.exports: $Exports<'jest-cli/build/generateEmptyCoverage'>; 140 | } 141 | declare module 'jest-cli/build/jest.js' { 142 | declare module.exports: $Exports<'jest-cli/build/jest'>; 143 | } 144 | declare module 'jest-cli/build/lib/BufferedConsole.js' { 145 | declare module.exports: $Exports<'jest-cli/build/lib/BufferedConsole'>; 146 | } 147 | declare module 'jest-cli/build/lib/formatTestResults.js' { 148 | declare module.exports: $Exports<'jest-cli/build/lib/formatTestResults'>; 149 | } 150 | declare module 'jest-cli/build/lib/promisify.js' { 151 | declare module.exports: $Exports<'jest-cli/build/lib/promisify'>; 152 | } 153 | declare module 'jest-cli/build/preRunMessage.js' { 154 | declare module.exports: $Exports<'jest-cli/build/preRunMessage'>; 155 | } 156 | declare module 'jest-cli/build/reporters/BaseReporter.js' { 157 | declare module.exports: $Exports<'jest-cli/build/reporters/BaseReporter'>; 158 | } 159 | declare module 'jest-cli/build/reporters/CoverageReporter.js' { 160 | declare module.exports: $Exports<'jest-cli/build/reporters/CoverageReporter'>; 161 | } 162 | declare module 'jest-cli/build/reporters/DefaultReporter.js' { 163 | declare module.exports: $Exports<'jest-cli/build/reporters/DefaultReporter'>; 164 | } 165 | declare module 'jest-cli/build/reporters/getConsoleOutput.js' { 166 | declare module.exports: $Exports<'jest-cli/build/reporters/getConsoleOutput'>; 167 | } 168 | declare module 'jest-cli/build/reporters/getResultHeader.js' { 169 | declare module.exports: $Exports<'jest-cli/build/reporters/getResultHeader'>; 170 | } 171 | declare module 'jest-cli/build/reporters/NotifyReporter.js' { 172 | declare module.exports: $Exports<'jest-cli/build/reporters/NotifyReporter'>; 173 | } 174 | declare module 'jest-cli/build/reporters/Status.js' { 175 | declare module.exports: $Exports<'jest-cli/build/reporters/Status'>; 176 | } 177 | declare module 'jest-cli/build/reporters/SummaryReporter.js' { 178 | declare module.exports: $Exports<'jest-cli/build/reporters/SummaryReporter'>; 179 | } 180 | declare module 'jest-cli/build/reporters/utils.js' { 181 | declare module.exports: $Exports<'jest-cli/build/reporters/utils'>; 182 | } 183 | declare module 'jest-cli/build/reporters/VerboseReporter.js' { 184 | declare module.exports: $Exports<'jest-cli/build/reporters/VerboseReporter'>; 185 | } 186 | declare module 'jest-cli/build/runTest.js' { 187 | declare module.exports: $Exports<'jest-cli/build/runTest'>; 188 | } 189 | declare module 'jest-cli/build/SearchSource.js' { 190 | declare module.exports: $Exports<'jest-cli/build/SearchSource'>; 191 | } 192 | declare module 'jest-cli/build/TestRunner.js' { 193 | declare module.exports: $Exports<'jest-cli/build/TestRunner'>; 194 | } 195 | declare module 'jest-cli/build/TestWatcher.js' { 196 | declare module.exports: $Exports<'jest-cli/build/TestWatcher'>; 197 | } 198 | declare module 'jest-cli/build/TestWorker.js' { 199 | declare module.exports: $Exports<'jest-cli/build/TestWorker'>; 200 | } 201 | -------------------------------------------------------------------------------- /flow-typed/npm/jsonwebtoken_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: a905a06e35e0528d2f19029c2da0afbd 2 | // flow-typed version: <>/jsonwebtoken_v^7.1.9/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'jsonwebtoken' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'jsonwebtoken' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'jsonwebtoken/decode' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'jsonwebtoken/lib/JsonWebTokenError' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'jsonwebtoken/lib/NotBeforeError' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'jsonwebtoken/lib/timespan' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'jsonwebtoken/lib/TokenExpiredError' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'jsonwebtoken/sign' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'jsonwebtoken/test/async_sign.tests' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'jsonwebtoken/test/buffer.tests' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'jsonwebtoken/test/encoding.tests' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'jsonwebtoken/test/expires_format.tests' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'jsonwebtoken/test/iat.tests' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'jsonwebtoken/test/invalid_exp.tests' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'jsonwebtoken/test/issue_147.tests' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'jsonwebtoken/test/issue_196.tests' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'jsonwebtoken/test/issue_70.tests' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'jsonwebtoken/test/jwt.hs.tests' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'jsonwebtoken/test/jwt.rs.tests' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'jsonwebtoken/test/non_object_values.tests' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'jsonwebtoken/test/noTimestamp.tests' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'jsonwebtoken/test/rsa-public-key.tests' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'jsonwebtoken/test/set_headers.tests' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'jsonwebtoken/test/undefined_secretOrPublickey.tests' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'jsonwebtoken/test/util/fakeDate' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'jsonwebtoken/test/verify.tests' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'jsonwebtoken/test/wrong_alg.tests' { 122 | declare module.exports: any; 123 | } 124 | 125 | declare module 'jsonwebtoken/verify' { 126 | declare module.exports: any; 127 | } 128 | 129 | // Filename aliases 130 | declare module 'jsonwebtoken/decode.js' { 131 | declare module.exports: $Exports<'jsonwebtoken/decode'>; 132 | } 133 | declare module 'jsonwebtoken/index' { 134 | declare module.exports: $Exports<'jsonwebtoken'>; 135 | } 136 | declare module 'jsonwebtoken/index.js' { 137 | declare module.exports: $Exports<'jsonwebtoken'>; 138 | } 139 | declare module 'jsonwebtoken/lib/JsonWebTokenError.js' { 140 | declare module.exports: $Exports<'jsonwebtoken/lib/JsonWebTokenError'>; 141 | } 142 | declare module 'jsonwebtoken/lib/NotBeforeError.js' { 143 | declare module.exports: $Exports<'jsonwebtoken/lib/NotBeforeError'>; 144 | } 145 | declare module 'jsonwebtoken/lib/timespan.js' { 146 | declare module.exports: $Exports<'jsonwebtoken/lib/timespan'>; 147 | } 148 | declare module 'jsonwebtoken/lib/TokenExpiredError.js' { 149 | declare module.exports: $Exports<'jsonwebtoken/lib/TokenExpiredError'>; 150 | } 151 | declare module 'jsonwebtoken/sign.js' { 152 | declare module.exports: $Exports<'jsonwebtoken/sign'>; 153 | } 154 | declare module 'jsonwebtoken/test/async_sign.tests.js' { 155 | declare module.exports: $Exports<'jsonwebtoken/test/async_sign.tests'>; 156 | } 157 | declare module 'jsonwebtoken/test/buffer.tests.js' { 158 | declare module.exports: $Exports<'jsonwebtoken/test/buffer.tests'>; 159 | } 160 | declare module 'jsonwebtoken/test/encoding.tests.js' { 161 | declare module.exports: $Exports<'jsonwebtoken/test/encoding.tests'>; 162 | } 163 | declare module 'jsonwebtoken/test/expires_format.tests.js' { 164 | declare module.exports: $Exports<'jsonwebtoken/test/expires_format.tests'>; 165 | } 166 | declare module 'jsonwebtoken/test/iat.tests.js' { 167 | declare module.exports: $Exports<'jsonwebtoken/test/iat.tests'>; 168 | } 169 | declare module 'jsonwebtoken/test/invalid_exp.tests.js' { 170 | declare module.exports: $Exports<'jsonwebtoken/test/invalid_exp.tests'>; 171 | } 172 | declare module 'jsonwebtoken/test/issue_147.tests.js' { 173 | declare module.exports: $Exports<'jsonwebtoken/test/issue_147.tests'>; 174 | } 175 | declare module 'jsonwebtoken/test/issue_196.tests.js' { 176 | declare module.exports: $Exports<'jsonwebtoken/test/issue_196.tests'>; 177 | } 178 | declare module 'jsonwebtoken/test/issue_70.tests.js' { 179 | declare module.exports: $Exports<'jsonwebtoken/test/issue_70.tests'>; 180 | } 181 | declare module 'jsonwebtoken/test/jwt.hs.tests.js' { 182 | declare module.exports: $Exports<'jsonwebtoken/test/jwt.hs.tests'>; 183 | } 184 | declare module 'jsonwebtoken/test/jwt.rs.tests.js' { 185 | declare module.exports: $Exports<'jsonwebtoken/test/jwt.rs.tests'>; 186 | } 187 | declare module 'jsonwebtoken/test/non_object_values.tests.js' { 188 | declare module.exports: $Exports<'jsonwebtoken/test/non_object_values.tests'>; 189 | } 190 | declare module 'jsonwebtoken/test/noTimestamp.tests.js' { 191 | declare module.exports: $Exports<'jsonwebtoken/test/noTimestamp.tests'>; 192 | } 193 | declare module 'jsonwebtoken/test/rsa-public-key.tests.js' { 194 | declare module.exports: $Exports<'jsonwebtoken/test/rsa-public-key.tests'>; 195 | } 196 | declare module 'jsonwebtoken/test/set_headers.tests.js' { 197 | declare module.exports: $Exports<'jsonwebtoken/test/set_headers.tests'>; 198 | } 199 | declare module 'jsonwebtoken/test/undefined_secretOrPublickey.tests.js' { 200 | declare module.exports: $Exports<'jsonwebtoken/test/undefined_secretOrPublickey.tests'>; 201 | } 202 | declare module 'jsonwebtoken/test/util/fakeDate.js' { 203 | declare module.exports: $Exports<'jsonwebtoken/test/util/fakeDate'>; 204 | } 205 | declare module 'jsonwebtoken/test/verify.tests.js' { 206 | declare module.exports: $Exports<'jsonwebtoken/test/verify.tests'>; 207 | } 208 | declare module 'jsonwebtoken/test/wrong_alg.tests.js' { 209 | declare module.exports: $Exports<'jsonwebtoken/test/wrong_alg.tests'>; 210 | } 211 | declare module 'jsonwebtoken/verify.js' { 212 | declare module.exports: $Exports<'jsonwebtoken/verify'>; 213 | } 214 | -------------------------------------------------------------------------------- /flow-typed/npm/mongodb_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 4508fc1cf66dfc8b4eb5e89cc79b74bb 2 | // flow-typed version: <>/mongodb_v^2.2.11/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'mongodb' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'mongodb' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'mongodb/boot_auth' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'mongodb/insert_bench' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'mongodb/lib/admin' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'mongodb/lib/aggregation_cursor' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'mongodb/lib/apm' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'mongodb/lib/bulk/common' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'mongodb/lib/bulk/ordered' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'mongodb/lib/bulk/unordered' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'mongodb/lib/collection' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'mongodb/lib/command_cursor' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'mongodb/lib/cursor' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'mongodb/lib/db' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'mongodb/lib/gridfs-stream/download' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'mongodb/lib/gridfs-stream/index' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'mongodb/lib/gridfs-stream/upload' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'mongodb/lib/gridfs/chunk' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'mongodb/lib/gridfs/grid_store' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'mongodb/lib/metadata' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'mongodb/lib/mongo_client' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'mongodb/lib/mongos' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'mongodb/lib/read_preference' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'mongodb/lib/replset' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'mongodb/lib/server' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'mongodb/lib/topology_base' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'mongodb/lib/url_parser' { 122 | declare module.exports: any; 123 | } 124 | 125 | declare module 'mongodb/lib/utils' { 126 | declare module.exports: any; 127 | } 128 | 129 | // Filename aliases 130 | declare module 'mongodb/boot_auth.js' { 131 | declare module.exports: $Exports<'mongodb/boot_auth'>; 132 | } 133 | declare module 'mongodb/index' { 134 | declare module.exports: $Exports<'mongodb'>; 135 | } 136 | declare module 'mongodb/index.js' { 137 | declare module.exports: $Exports<'mongodb'>; 138 | } 139 | declare module 'mongodb/insert_bench.js' { 140 | declare module.exports: $Exports<'mongodb/insert_bench'>; 141 | } 142 | declare module 'mongodb/lib/admin.js' { 143 | declare module.exports: $Exports<'mongodb/lib/admin'>; 144 | } 145 | declare module 'mongodb/lib/aggregation_cursor.js' { 146 | declare module.exports: $Exports<'mongodb/lib/aggregation_cursor'>; 147 | } 148 | declare module 'mongodb/lib/apm.js' { 149 | declare module.exports: $Exports<'mongodb/lib/apm'>; 150 | } 151 | declare module 'mongodb/lib/bulk/common.js' { 152 | declare module.exports: $Exports<'mongodb/lib/bulk/common'>; 153 | } 154 | declare module 'mongodb/lib/bulk/ordered.js' { 155 | declare module.exports: $Exports<'mongodb/lib/bulk/ordered'>; 156 | } 157 | declare module 'mongodb/lib/bulk/unordered.js' { 158 | declare module.exports: $Exports<'mongodb/lib/bulk/unordered'>; 159 | } 160 | declare module 'mongodb/lib/collection.js' { 161 | declare module.exports: $Exports<'mongodb/lib/collection'>; 162 | } 163 | declare module 'mongodb/lib/command_cursor.js' { 164 | declare module.exports: $Exports<'mongodb/lib/command_cursor'>; 165 | } 166 | declare module 'mongodb/lib/cursor.js' { 167 | declare module.exports: $Exports<'mongodb/lib/cursor'>; 168 | } 169 | declare module 'mongodb/lib/db.js' { 170 | declare module.exports: $Exports<'mongodb/lib/db'>; 171 | } 172 | declare module 'mongodb/lib/gridfs-stream/download.js' { 173 | declare module.exports: $Exports<'mongodb/lib/gridfs-stream/download'>; 174 | } 175 | declare module 'mongodb/lib/gridfs-stream/index.js' { 176 | declare module.exports: $Exports<'mongodb/lib/gridfs-stream/index'>; 177 | } 178 | declare module 'mongodb/lib/gridfs-stream/upload.js' { 179 | declare module.exports: $Exports<'mongodb/lib/gridfs-stream/upload'>; 180 | } 181 | declare module 'mongodb/lib/gridfs/chunk.js' { 182 | declare module.exports: $Exports<'mongodb/lib/gridfs/chunk'>; 183 | } 184 | declare module 'mongodb/lib/gridfs/grid_store.js' { 185 | declare module.exports: $Exports<'mongodb/lib/gridfs/grid_store'>; 186 | } 187 | declare module 'mongodb/lib/metadata.js' { 188 | declare module.exports: $Exports<'mongodb/lib/metadata'>; 189 | } 190 | declare module 'mongodb/lib/mongo_client.js' { 191 | declare module.exports: $Exports<'mongodb/lib/mongo_client'>; 192 | } 193 | declare module 'mongodb/lib/mongos.js' { 194 | declare module.exports: $Exports<'mongodb/lib/mongos'>; 195 | } 196 | declare module 'mongodb/lib/read_preference.js' { 197 | declare module.exports: $Exports<'mongodb/lib/read_preference'>; 198 | } 199 | declare module 'mongodb/lib/replset.js' { 200 | declare module.exports: $Exports<'mongodb/lib/replset'>; 201 | } 202 | declare module 'mongodb/lib/server.js' { 203 | declare module.exports: $Exports<'mongodb/lib/server'>; 204 | } 205 | declare module 'mongodb/lib/topology_base.js' { 206 | declare module.exports: $Exports<'mongodb/lib/topology_base'>; 207 | } 208 | declare module 'mongodb/lib/url_parser.js' { 209 | declare module.exports: $Exports<'mongodb/lib/url_parser'>; 210 | } 211 | declare module 'mongodb/lib/utils.js' { 212 | declare module.exports: $Exports<'mongodb/lib/utils'>; 213 | } 214 | -------------------------------------------------------------------------------- /flow-typed/npm/mongojs_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 1e6098c2749ca2dda99236fdf7b86472 2 | // flow-typed version: <>/mongojs_v^2.4.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'mongojs' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'mongojs' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'mongojs/lib/bulk' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'mongojs/lib/collection' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'mongojs/lib/cursor' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'mongojs/lib/database' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'mongojs/release' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'mongojs/test/crash' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'mongojs/test/insert' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'mongojs/test/tape' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'mongojs/test/test-aggregate-options' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'mongojs/test/test-aggregate-pipeline' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'mongojs/test/test-aggregate' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'mongojs/test/test-bulk-replace-one' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'mongojs/test/test-bulk-updates' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'mongojs/test/test-chained-collection-names' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'mongojs/test/test-close-unopened' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'mongojs/test/test-connect-non-existing-db' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'mongojs/test/test-connection-strings' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'mongojs/test/test-crash' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'mongojs/test/test-create-collection' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'mongojs/test/test-cursor-count' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'mongojs/test/test-cursor-explain' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'mongojs/test/test-cursor-foreach' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'mongojs/test/test-cursor-map' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'mongojs/test/test-cursor-rewind' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'mongojs/test/test-cursor-size' { 122 | declare module.exports: any; 123 | } 124 | 125 | declare module 'mongojs/test/test-db-stats' { 126 | declare module.exports: any; 127 | } 128 | 129 | declare module 'mongojs/test/test-disinct' { 130 | declare module.exports: any; 131 | } 132 | 133 | declare module 'mongojs/test/test-drop-database' { 134 | declare module.exports: any; 135 | } 136 | 137 | declare module 'mongojs/test/test-drop-indexes' { 138 | declare module.exports: any; 139 | } 140 | 141 | declare module 'mongojs/test/test-empty-bulk' { 142 | declare module.exports: any; 143 | } 144 | 145 | declare module 'mongojs/test/test-eval' { 146 | declare module.exports: any; 147 | } 148 | 149 | declare module 'mongojs/test/test-event-close' { 150 | declare module.exports: any; 151 | } 152 | 153 | declare module 'mongojs/test/test-event-connect' { 154 | declare module.exports: any; 155 | } 156 | 157 | declare module 'mongojs/test/test-expose-bson-types' { 158 | declare module.exports: any; 159 | } 160 | 161 | declare module 'mongojs/test/test-find-and-modify' { 162 | declare module.exports: any; 163 | } 164 | 165 | declare module 'mongojs/test/test-find-and-select' { 166 | declare module.exports: any; 167 | } 168 | 169 | declare module 'mongojs/test/test-find-by-objectid' { 170 | declare module.exports: any; 171 | } 172 | 173 | declare module 'mongojs/test/test-find-cursor-options' { 174 | declare module.exports: any; 175 | } 176 | 177 | declare module 'mongojs/test/test-find-cursor' { 178 | declare module.exports: any; 179 | } 180 | 181 | declare module 'mongojs/test/test-find-dot-collection' { 182 | declare module.exports: any; 183 | } 184 | 185 | declare module 'mongojs/test/test-find-large-collection' { 186 | declare module.exports: any; 187 | } 188 | 189 | declare module 'mongojs/test/test-find-limit' { 190 | declare module.exports: any; 191 | } 192 | 193 | declare module 'mongojs/test/test-find-one' { 194 | declare module.exports: any; 195 | } 196 | 197 | declare module 'mongojs/test/test-find-query' { 198 | declare module.exports: any; 199 | } 200 | 201 | declare module 'mongojs/test/test-find-sort-many' { 202 | declare module.exports: any; 203 | } 204 | 205 | declare module 'mongojs/test/test-find-sort' { 206 | declare module.exports: any; 207 | } 208 | 209 | declare module 'mongojs/test/test-find-tailable' { 210 | declare module.exports: any; 211 | } 212 | 213 | declare module 'mongojs/test/test-find' { 214 | declare module.exports: any; 215 | } 216 | 217 | declare module 'mongojs/test/test-get-collection-names' { 218 | declare module.exports: any; 219 | } 220 | 221 | declare module 'mongojs/test/test-group' { 222 | declare module.exports: any; 223 | } 224 | 225 | declare module 'mongojs/test/test-insert-duplicate-key' { 226 | declare module.exports: any; 227 | } 228 | 229 | declare module 'mongojs/test/test-insert' { 230 | declare module.exports: any; 231 | } 232 | 233 | declare module 'mongojs/test/test-iscapped' { 234 | declare module.exports: any; 235 | } 236 | 237 | declare module 'mongojs/test/test-map-reduce' { 238 | declare module.exports: any; 239 | } 240 | 241 | declare module 'mongojs/test/test-mongojs-exports' { 242 | declare module.exports: any; 243 | } 244 | 245 | declare module 'mongojs/test/test-optional-callback' { 246 | declare module.exports: any; 247 | } 248 | 249 | declare module 'mongojs/test/test-pass-mongodb' { 250 | declare module.exports: any; 251 | } 252 | 253 | declare module 'mongojs/test/test-pass-mongojs' { 254 | declare module.exports: any; 255 | } 256 | 257 | declare module 'mongojs/test/test-proxy' { 258 | declare module.exports: any; 259 | } 260 | 261 | declare module 'mongojs/test/test-remove' { 262 | declare module.exports: any; 263 | } 264 | 265 | declare module 'mongojs/test/test-rename-collection' { 266 | declare module.exports: any; 267 | } 268 | 269 | declare module 'mongojs/test/test-runcommand' { 270 | declare module.exports: any; 271 | } 272 | 273 | declare module 'mongojs/test/test-save' { 274 | declare module.exports: any; 275 | } 276 | 277 | declare module 'mongojs/test/test-simple' { 278 | declare module.exports: any; 279 | } 280 | 281 | declare module 'mongojs/test/test-stats' { 282 | declare module.exports: any; 283 | } 284 | 285 | declare module 'mongojs/test/test-streaming-cursor' { 286 | declare module.exports: any; 287 | } 288 | 289 | declare module 'mongojs/test/test-to-json-bulk' { 290 | declare module.exports: any; 291 | } 292 | 293 | declare module 'mongojs/test/test-to-string-bulk' { 294 | declare module.exports: any; 295 | } 296 | 297 | declare module 'mongojs/test/test-to-string-mongos' { 298 | declare module.exports: any; 299 | } 300 | 301 | declare module 'mongojs/test/test-to-string-replica-set' { 302 | declare module.exports: any; 303 | } 304 | 305 | declare module 'mongojs/test/test-to-string' { 306 | declare module.exports: any; 307 | } 308 | 309 | declare module 'mongojs/test/test-update-and-callback' { 310 | declare module.exports: any; 311 | } 312 | 313 | declare module 'mongojs/test/test-update-multi' { 314 | declare module.exports: any; 315 | } 316 | 317 | declare module 'mongojs/test/test-update' { 318 | declare module.exports: any; 319 | } 320 | 321 | declare module 'mongojs/test/test-user-create' { 322 | declare module.exports: any; 323 | } 324 | 325 | declare module 'mongojs/test/test-user-drop' { 326 | declare module.exports: any; 327 | } 328 | 329 | // Filename aliases 330 | declare module 'mongojs/index' { 331 | declare module.exports: $Exports<'mongojs'>; 332 | } 333 | declare module 'mongojs/index.js' { 334 | declare module.exports: $Exports<'mongojs'>; 335 | } 336 | declare module 'mongojs/lib/bulk.js' { 337 | declare module.exports: $Exports<'mongojs/lib/bulk'>; 338 | } 339 | declare module 'mongojs/lib/collection.js' { 340 | declare module.exports: $Exports<'mongojs/lib/collection'>; 341 | } 342 | declare module 'mongojs/lib/cursor.js' { 343 | declare module.exports: $Exports<'mongojs/lib/cursor'>; 344 | } 345 | declare module 'mongojs/lib/database.js' { 346 | declare module.exports: $Exports<'mongojs/lib/database'>; 347 | } 348 | declare module 'mongojs/release.js' { 349 | declare module.exports: $Exports<'mongojs/release'>; 350 | } 351 | declare module 'mongojs/test/crash.js' { 352 | declare module.exports: $Exports<'mongojs/test/crash'>; 353 | } 354 | declare module 'mongojs/test/insert.js' { 355 | declare module.exports: $Exports<'mongojs/test/insert'>; 356 | } 357 | declare module 'mongojs/test/tape.js' { 358 | declare module.exports: $Exports<'mongojs/test/tape'>; 359 | } 360 | declare module 'mongojs/test/test-aggregate-options.js' { 361 | declare module.exports: $Exports<'mongojs/test/test-aggregate-options'>; 362 | } 363 | declare module 'mongojs/test/test-aggregate-pipeline.js' { 364 | declare module.exports: $Exports<'mongojs/test/test-aggregate-pipeline'>; 365 | } 366 | declare module 'mongojs/test/test-aggregate.js' { 367 | declare module.exports: $Exports<'mongojs/test/test-aggregate'>; 368 | } 369 | declare module 'mongojs/test/test-bulk-replace-one.js' { 370 | declare module.exports: $Exports<'mongojs/test/test-bulk-replace-one'>; 371 | } 372 | declare module 'mongojs/test/test-bulk-updates.js' { 373 | declare module.exports: $Exports<'mongojs/test/test-bulk-updates'>; 374 | } 375 | declare module 'mongojs/test/test-chained-collection-names.js' { 376 | declare module.exports: $Exports<'mongojs/test/test-chained-collection-names'>; 377 | } 378 | declare module 'mongojs/test/test-close-unopened.js' { 379 | declare module.exports: $Exports<'mongojs/test/test-close-unopened'>; 380 | } 381 | declare module 'mongojs/test/test-connect-non-existing-db.js' { 382 | declare module.exports: $Exports<'mongojs/test/test-connect-non-existing-db'>; 383 | } 384 | declare module 'mongojs/test/test-connection-strings.js' { 385 | declare module.exports: $Exports<'mongojs/test/test-connection-strings'>; 386 | } 387 | declare module 'mongojs/test/test-crash.js' { 388 | declare module.exports: $Exports<'mongojs/test/test-crash'>; 389 | } 390 | declare module 'mongojs/test/test-create-collection.js' { 391 | declare module.exports: $Exports<'mongojs/test/test-create-collection'>; 392 | } 393 | declare module 'mongojs/test/test-cursor-count.js' { 394 | declare module.exports: $Exports<'mongojs/test/test-cursor-count'>; 395 | } 396 | declare module 'mongojs/test/test-cursor-explain.js' { 397 | declare module.exports: $Exports<'mongojs/test/test-cursor-explain'>; 398 | } 399 | declare module 'mongojs/test/test-cursor-foreach.js' { 400 | declare module.exports: $Exports<'mongojs/test/test-cursor-foreach'>; 401 | } 402 | declare module 'mongojs/test/test-cursor-map.js' { 403 | declare module.exports: $Exports<'mongojs/test/test-cursor-map'>; 404 | } 405 | declare module 'mongojs/test/test-cursor-rewind.js' { 406 | declare module.exports: $Exports<'mongojs/test/test-cursor-rewind'>; 407 | } 408 | declare module 'mongojs/test/test-cursor-size.js' { 409 | declare module.exports: $Exports<'mongojs/test/test-cursor-size'>; 410 | } 411 | declare module 'mongojs/test/test-db-stats.js' { 412 | declare module.exports: $Exports<'mongojs/test/test-db-stats'>; 413 | } 414 | declare module 'mongojs/test/test-disinct.js' { 415 | declare module.exports: $Exports<'mongojs/test/test-disinct'>; 416 | } 417 | declare module 'mongojs/test/test-drop-database.js' { 418 | declare module.exports: $Exports<'mongojs/test/test-drop-database'>; 419 | } 420 | declare module 'mongojs/test/test-drop-indexes.js' { 421 | declare module.exports: $Exports<'mongojs/test/test-drop-indexes'>; 422 | } 423 | declare module 'mongojs/test/test-empty-bulk.js' { 424 | declare module.exports: $Exports<'mongojs/test/test-empty-bulk'>; 425 | } 426 | declare module 'mongojs/test/test-eval.js' { 427 | declare module.exports: $Exports<'mongojs/test/test-eval'>; 428 | } 429 | declare module 'mongojs/test/test-event-close.js' { 430 | declare module.exports: $Exports<'mongojs/test/test-event-close'>; 431 | } 432 | declare module 'mongojs/test/test-event-connect.js' { 433 | declare module.exports: $Exports<'mongojs/test/test-event-connect'>; 434 | } 435 | declare module 'mongojs/test/test-expose-bson-types.js' { 436 | declare module.exports: $Exports<'mongojs/test/test-expose-bson-types'>; 437 | } 438 | declare module 'mongojs/test/test-find-and-modify.js' { 439 | declare module.exports: $Exports<'mongojs/test/test-find-and-modify'>; 440 | } 441 | declare module 'mongojs/test/test-find-and-select.js' { 442 | declare module.exports: $Exports<'mongojs/test/test-find-and-select'>; 443 | } 444 | declare module 'mongojs/test/test-find-by-objectid.js' { 445 | declare module.exports: $Exports<'mongojs/test/test-find-by-objectid'>; 446 | } 447 | declare module 'mongojs/test/test-find-cursor-options.js' { 448 | declare module.exports: $Exports<'mongojs/test/test-find-cursor-options'>; 449 | } 450 | declare module 'mongojs/test/test-find-cursor.js' { 451 | declare module.exports: $Exports<'mongojs/test/test-find-cursor'>; 452 | } 453 | declare module 'mongojs/test/test-find-dot-collection.js' { 454 | declare module.exports: $Exports<'mongojs/test/test-find-dot-collection'>; 455 | } 456 | declare module 'mongojs/test/test-find-large-collection.js' { 457 | declare module.exports: $Exports<'mongojs/test/test-find-large-collection'>; 458 | } 459 | declare module 'mongojs/test/test-find-limit.js' { 460 | declare module.exports: $Exports<'mongojs/test/test-find-limit'>; 461 | } 462 | declare module 'mongojs/test/test-find-one.js' { 463 | declare module.exports: $Exports<'mongojs/test/test-find-one'>; 464 | } 465 | declare module 'mongojs/test/test-find-query.js' { 466 | declare module.exports: $Exports<'mongojs/test/test-find-query'>; 467 | } 468 | declare module 'mongojs/test/test-find-sort-many.js' { 469 | declare module.exports: $Exports<'mongojs/test/test-find-sort-many'>; 470 | } 471 | declare module 'mongojs/test/test-find-sort.js' { 472 | declare module.exports: $Exports<'mongojs/test/test-find-sort'>; 473 | } 474 | declare module 'mongojs/test/test-find-tailable.js' { 475 | declare module.exports: $Exports<'mongojs/test/test-find-tailable'>; 476 | } 477 | declare module 'mongojs/test/test-find.js' { 478 | declare module.exports: $Exports<'mongojs/test/test-find'>; 479 | } 480 | declare module 'mongojs/test/test-get-collection-names.js' { 481 | declare module.exports: $Exports<'mongojs/test/test-get-collection-names'>; 482 | } 483 | declare module 'mongojs/test/test-group.js' { 484 | declare module.exports: $Exports<'mongojs/test/test-group'>; 485 | } 486 | declare module 'mongojs/test/test-insert-duplicate-key.js' { 487 | declare module.exports: $Exports<'mongojs/test/test-insert-duplicate-key'>; 488 | } 489 | declare module 'mongojs/test/test-insert.js' { 490 | declare module.exports: $Exports<'mongojs/test/test-insert'>; 491 | } 492 | declare module 'mongojs/test/test-iscapped.js' { 493 | declare module.exports: $Exports<'mongojs/test/test-iscapped'>; 494 | } 495 | declare module 'mongojs/test/test-map-reduce.js' { 496 | declare module.exports: $Exports<'mongojs/test/test-map-reduce'>; 497 | } 498 | declare module 'mongojs/test/test-mongojs-exports.js' { 499 | declare module.exports: $Exports<'mongojs/test/test-mongojs-exports'>; 500 | } 501 | declare module 'mongojs/test/test-optional-callback.js' { 502 | declare module.exports: $Exports<'mongojs/test/test-optional-callback'>; 503 | } 504 | declare module 'mongojs/test/test-pass-mongodb.js' { 505 | declare module.exports: $Exports<'mongojs/test/test-pass-mongodb'>; 506 | } 507 | declare module 'mongojs/test/test-pass-mongojs.js' { 508 | declare module.exports: $Exports<'mongojs/test/test-pass-mongojs'>; 509 | } 510 | declare module 'mongojs/test/test-proxy.js' { 511 | declare module.exports: $Exports<'mongojs/test/test-proxy'>; 512 | } 513 | declare module 'mongojs/test/test-remove.js' { 514 | declare module.exports: $Exports<'mongojs/test/test-remove'>; 515 | } 516 | declare module 'mongojs/test/test-rename-collection.js' { 517 | declare module.exports: $Exports<'mongojs/test/test-rename-collection'>; 518 | } 519 | declare module 'mongojs/test/test-runcommand.js' { 520 | declare module.exports: $Exports<'mongojs/test/test-runcommand'>; 521 | } 522 | declare module 'mongojs/test/test-save.js' { 523 | declare module.exports: $Exports<'mongojs/test/test-save'>; 524 | } 525 | declare module 'mongojs/test/test-simple.js' { 526 | declare module.exports: $Exports<'mongojs/test/test-simple'>; 527 | } 528 | declare module 'mongojs/test/test-stats.js' { 529 | declare module.exports: $Exports<'mongojs/test/test-stats'>; 530 | } 531 | declare module 'mongojs/test/test-streaming-cursor.js' { 532 | declare module.exports: $Exports<'mongojs/test/test-streaming-cursor'>; 533 | } 534 | declare module 'mongojs/test/test-to-json-bulk.js' { 535 | declare module.exports: $Exports<'mongojs/test/test-to-json-bulk'>; 536 | } 537 | declare module 'mongojs/test/test-to-string-bulk.js' { 538 | declare module.exports: $Exports<'mongojs/test/test-to-string-bulk'>; 539 | } 540 | declare module 'mongojs/test/test-to-string-mongos.js' { 541 | declare module.exports: $Exports<'mongojs/test/test-to-string-mongos'>; 542 | } 543 | declare module 'mongojs/test/test-to-string-replica-set.js' { 544 | declare module.exports: $Exports<'mongojs/test/test-to-string-replica-set'>; 545 | } 546 | declare module 'mongojs/test/test-to-string.js' { 547 | declare module.exports: $Exports<'mongojs/test/test-to-string'>; 548 | } 549 | declare module 'mongojs/test/test-update-and-callback.js' { 550 | declare module.exports: $Exports<'mongojs/test/test-update-and-callback'>; 551 | } 552 | declare module 'mongojs/test/test-update-multi.js' { 553 | declare module.exports: $Exports<'mongojs/test/test-update-multi'>; 554 | } 555 | declare module 'mongojs/test/test-update.js' { 556 | declare module.exports: $Exports<'mongojs/test/test-update'>; 557 | } 558 | declare module 'mongojs/test/test-user-create.js' { 559 | declare module.exports: $Exports<'mongojs/test/test-user-create'>; 560 | } 561 | declare module 'mongojs/test/test-user-drop.js' { 562 | declare module.exports: $Exports<'mongojs/test/test-user-drop'>; 563 | } 564 | -------------------------------------------------------------------------------- /flow-typed/npm/node-fetch_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 5221607a0a437e5a803ec92a3bd22dd5 2 | // flow-typed version: <>/node-fetch_v^1.6.3/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'node-fetch' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'node-fetch' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'node-fetch/lib/body' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'node-fetch/lib/fetch-error' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'node-fetch/lib/headers' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'node-fetch/lib/request' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'node-fetch/lib/response' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'node-fetch/test/server' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'node-fetch/test/test' { 50 | declare module.exports: any; 51 | } 52 | 53 | // Filename aliases 54 | declare module 'node-fetch/index' { 55 | declare module.exports: $Exports<'node-fetch'>; 56 | } 57 | declare module 'node-fetch/index.js' { 58 | declare module.exports: $Exports<'node-fetch'>; 59 | } 60 | declare module 'node-fetch/lib/body.js' { 61 | declare module.exports: $Exports<'node-fetch/lib/body'>; 62 | } 63 | declare module 'node-fetch/lib/fetch-error.js' { 64 | declare module.exports: $Exports<'node-fetch/lib/fetch-error'>; 65 | } 66 | declare module 'node-fetch/lib/headers.js' { 67 | declare module.exports: $Exports<'node-fetch/lib/headers'>; 68 | } 69 | declare module 'node-fetch/lib/request.js' { 70 | declare module.exports: $Exports<'node-fetch/lib/request'>; 71 | } 72 | declare module 'node-fetch/lib/response.js' { 73 | declare module.exports: $Exports<'node-fetch/lib/response'>; 74 | } 75 | declare module 'node-fetch/test/server.js' { 76 | declare module.exports: $Exports<'node-fetch/test/server'>; 77 | } 78 | declare module 'node-fetch/test/test.js' { 79 | declare module.exports: $Exports<'node-fetch/test/test'>; 80 | } 81 | -------------------------------------------------------------------------------- /flow-typed/npm/pg-then_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 837fab571d888d068bb176c30658ac39 2 | // flow-typed version: <>/pg-then_v^1.2.1/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'pg-then' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'pg-then' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | 26 | 27 | // Filename aliases 28 | declare module 'pg-then/index' { 29 | declare module.exports: $Exports<'pg-then'>; 30 | } 31 | declare module 'pg-then/index.js' { 32 | declare module.exports: $Exports<'pg-then'>; 33 | } 34 | -------------------------------------------------------------------------------- /flow-typed/npm/pg_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: f2b77645a154ad935fbe6a4be8acc9bf 2 | // flow-typed version: <>/pg_v^6.1.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'pg' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'pg' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'pg/lib/client' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'pg/lib/connection-parameters' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'pg/lib/connection' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'pg/lib/defaults' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'pg/lib/index' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'pg/lib/native/index' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'pg/lib/native/query' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'pg/lib/native/result' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'pg/lib/pool-factory' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'pg/lib/query' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'pg/lib/result' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'pg/lib/type-overrides' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'pg/lib/utils' { 74 | declare module.exports: any; 75 | } 76 | 77 | // Filename aliases 78 | declare module 'pg/lib/client.js' { 79 | declare module.exports: $Exports<'pg/lib/client'>; 80 | } 81 | declare module 'pg/lib/connection-parameters.js' { 82 | declare module.exports: $Exports<'pg/lib/connection-parameters'>; 83 | } 84 | declare module 'pg/lib/connection.js' { 85 | declare module.exports: $Exports<'pg/lib/connection'>; 86 | } 87 | declare module 'pg/lib/defaults.js' { 88 | declare module.exports: $Exports<'pg/lib/defaults'>; 89 | } 90 | declare module 'pg/lib/index.js' { 91 | declare module.exports: $Exports<'pg/lib/index'>; 92 | } 93 | declare module 'pg/lib/native/index.js' { 94 | declare module.exports: $Exports<'pg/lib/native/index'>; 95 | } 96 | declare module 'pg/lib/native/query.js' { 97 | declare module.exports: $Exports<'pg/lib/native/query'>; 98 | } 99 | declare module 'pg/lib/native/result.js' { 100 | declare module.exports: $Exports<'pg/lib/native/result'>; 101 | } 102 | declare module 'pg/lib/pool-factory.js' { 103 | declare module.exports: $Exports<'pg/lib/pool-factory'>; 104 | } 105 | declare module 'pg/lib/query.js' { 106 | declare module.exports: $Exports<'pg/lib/query'>; 107 | } 108 | declare module 'pg/lib/result.js' { 109 | declare module.exports: $Exports<'pg/lib/result'>; 110 | } 111 | declare module 'pg/lib/type-overrides.js' { 112 | declare module.exports: $Exports<'pg/lib/type-overrides'>; 113 | } 114 | declare module 'pg/lib/utils.js' { 115 | declare module.exports: $Exports<'pg/lib/utils'>; 116 | } 117 | -------------------------------------------------------------------------------- /flow-typed/npm/request_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 948321f3738ae9e384c582b3ab1df5e4 2 | // flow-typed version: <>/request_v^2.72.0/flow_v0.34.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'request' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'request' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'request/lib/auth' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'request/lib/cookies' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'request/lib/getProxyFromURI' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'request/lib/har' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'request/lib/helpers' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'request/lib/multipart' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'request/lib/oauth' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'request/lib/querystring' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'request/lib/redirect' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'request/lib/tunnel' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'request/request' { 66 | declare module.exports: any; 67 | } 68 | 69 | // Filename aliases 70 | declare module 'request/index' { 71 | declare module.exports: $Exports<'request'>; 72 | } 73 | declare module 'request/index.js' { 74 | declare module.exports: $Exports<'request'>; 75 | } 76 | declare module 'request/lib/auth.js' { 77 | declare module.exports: $Exports<'request/lib/auth'>; 78 | } 79 | declare module 'request/lib/cookies.js' { 80 | declare module.exports: $Exports<'request/lib/cookies'>; 81 | } 82 | declare module 'request/lib/getProxyFromURI.js' { 83 | declare module.exports: $Exports<'request/lib/getProxyFromURI'>; 84 | } 85 | declare module 'request/lib/har.js' { 86 | declare module.exports: $Exports<'request/lib/har'>; 87 | } 88 | declare module 'request/lib/helpers.js' { 89 | declare module.exports: $Exports<'request/lib/helpers'>; 90 | } 91 | declare module 'request/lib/multipart.js' { 92 | declare module.exports: $Exports<'request/lib/multipart'>; 93 | } 94 | declare module 'request/lib/oauth.js' { 95 | declare module.exports: $Exports<'request/lib/oauth'>; 96 | } 97 | declare module 'request/lib/querystring.js' { 98 | declare module.exports: $Exports<'request/lib/querystring'>; 99 | } 100 | declare module 'request/lib/redirect.js' { 101 | declare module.exports: $Exports<'request/lib/redirect'>; 102 | } 103 | declare module 'request/lib/tunnel.js' { 104 | declare module.exports: $Exports<'request/lib/tunnel'>; 105 | } 106 | declare module 'request/request.js' { 107 | declare module.exports: $Exports<'request/request'>; 108 | } 109 | -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artsy/Mitosis/5cfad595d22a3365551dccd7cbd9feb321b8d6f0/images/screenshot.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=759670 3 | // for the documentation about the jsconfig.json format 4 | "compilerOptions": { 5 | "target": "es2015", 6 | "module": "commonjs", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "exclude": [ 10 | "node_modules", 11 | "bower_components", 12 | "jspm_packages", 13 | "tmp", 14 | "temp", 15 | "distribution" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mitosis", 3 | "version": "1.0.0", 4 | "description": "Bots for Art", 5 | "scripts": { 6 | "start": "babel-watch --watch views source/mitosis.js", 7 | "lint": "jshint --exclude node_modules .", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "postinstall": "babel source --out-dir distribution --source-maps", 10 | "hourly": "babel-node source/scripts/hourly.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/fbsamples/messenger-platform-samples.git" 15 | }, 16 | "author": "Facebook", 17 | "license": "ISC", 18 | "dependencies": { 19 | "babel-cli": "^6.18.0", 20 | "babel-eslint": "^7.1.0", 21 | "babel-plugin-syntax-async-functions": "^6.13.0", 22 | "babel-plugin-transform-flow-strip-types": "^6.18.0", 23 | "babel-plugin-transform-regenerator": "^6.16.1", 24 | "babel-polyfill": "^6.16.0", 25 | "babel-preset-es2015": "^6.18.0", 26 | "babel-preset-stage-3": "^6.17.0", 27 | "body-parser": "^1.15.0", 28 | "config": "^1.20.4", 29 | "ejs": "^2.4.2", 30 | "express": "^4.13.4", 31 | "jsonwebtoken": "^7.1.9", 32 | "mongodb": "^2.2.11", 33 | "mongojs": "^2.4.0", 34 | "node-fetch": "^1.6.3", 35 | "places": "https://github.com/artsy/places.git#orta-tz" 36 | }, 37 | "engines": { 38 | "node": "7.1.0" 39 | }, 40 | "devDependencies": { 41 | "babel-watch": "^2.0.3", 42 | "eslint": "^3.9.1", 43 | "eslint-config-standard": "^6.2.1", 44 | "eslint-plugin-flowtype": "^2.25.0", 45 | "eslint-plugin-import": "^2.2.0", 46 | "eslint-plugin-promise": "^3.3.1", 47 | "eslint-plugin-standard": "^2.0.1", 48 | "flow-bin": "^0.34.0", 49 | "jest-cli": "^16.0.2" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Messenger Demo 4 | 5 | 6 | 23 | 24 |

Messenger Demo

25 | 26 |
27 |

The "Send to Messenger" plugin will trigger an authentication callback to your webhook.

28 | 29 |
35 |
36 |
37 | 38 |
39 |

The "Message Us" plugin takes the user directly to Messenger and into a thread with your Page.

40 | 41 |
46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /source/bot/artsy-api.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import fetch from "node-fetch" 4 | import jwt from "jsonwebtoken" 5 | import { ARTSY_API_CLIENT, ARTSY_API_SECRET, GRAVITY_URL, METAPHYSICS_URL } from "../globals" 6 | import { updateMitosisUser } from "../db/mongo" 7 | import type { GraphQLQuery, MitosisUser } from "./types" 8 | const querystring = require("querystring") 9 | 10 | /** Represents the URL to start the auth flow */ 11 | export const oauthLoginURL = (redirect: string) => `${GRAVITY_URL}/oauth2/authorize?client_id=${ARTSY_API_CLIENT}&redirect_uri=${redirect}&response_type=code` 12 | 13 | // These are collated for you Joey 14 | 15 | export const GravityTrendingArtistsAPI = "/api/v1/artists/trending" 16 | export const GravityEmergingArtistsAPI = "/api/v1/artists/emerging" 17 | export const GravityAccessTokenAPI = "/oauth2/access_token" 18 | export const GravityXappAPI = "/api/v1/xapp_token" 19 | export const GravityMeAPI = "/api/v1/me" 20 | export const GravityShowsSearchAPI = "/api/v1/shows" 21 | 22 | /** 23 | * Get the API header creds required to submit API requests 24 | * 25 | * @param {MitosisUser} user 26 | * @returns {*} either an access token header, or xapp token 27 | */ 28 | function headerAuthParams(user: MitosisUser): any { 29 | if (user.userToken !== undefined) { 30 | return { "X-Access-Token": user.userToken } 31 | } 32 | else if (user.xappToken !== undefined) { 33 | return { "X-Xapp-Token": user.xappToken } 34 | } else { 35 | console.warn(`Error generating header params for ${user.fbSenderID}`) 36 | return {} 37 | } 38 | } 39 | 40 | /** 41 | * Updates either the Xapp or OAuth2 token for a user 42 | * should be called before every Artsy API request 43 | * 44 | * @param {MitosisUser} user 45 | */ 46 | async function ensureAuthenticationCredentials(user: MitosisUser) { 47 | if (isAuthenticationIsUpToDate(user)) { return } 48 | 49 | if (user.artsyOauthAppCode !== undefined) { 50 | const data = await getOAuthToken(user) 51 | user.userToken = data.access_token 52 | await updateMitosisUser(user) 53 | } else { 54 | const data = await getXappToken() 55 | user.xappToken = data.xapp_token 56 | await updateMitosisUser(user) 57 | } 58 | } 59 | 60 | /** 61 | * Gets an Artsy XApp token 62 | * @returns {Promise} json representation from gravity 63 | */ 64 | export async function getXappToken(): Promise { 65 | const clientAndSecret = `client_id=${ARTSY_API_CLIENT}&client_secret=${ARTSY_API_SECRET}` 66 | return fetch(`${GRAVITY_URL}${GravityXappAPI}?${clientAndSecret}`) 67 | .then(checkStatus) 68 | .then(parseJSON) 69 | } 70 | 71 | /** 72 | * Gets an Artsy Oauth token 73 | * @returns {Promise} json representation from gravity 74 | */ 75 | export async function getOAuthToken(user: MitosisUser): Promise { 76 | const oauthCode = user.artsyOauthAppCode 77 | const query = querystring.stringify({ 78 | client_id: ARTSY_API_CLIENT, 79 | client_secret: ARTSY_API_SECRET, 80 | code: oauthCode, 81 | grant_type: "authorization_code" 82 | }) 83 | 84 | return fetch(`${GRAVITY_URL}${GravityAccessTokenAPI}?${query}`) 85 | .then(checkStatus) 86 | .then(parseJSON) 87 | } 88 | 89 | /** 90 | * Creates a query to metaphysics, returns the JSON data 91 | * 92 | * @param {string} query the request 93 | * @returns {Promise} 94 | */ 95 | export async function metaphysicsQuery(query: GraphQLQuery, user: MitosisUser): Promise { 96 | await ensureAuthenticationCredentials(user) 97 | return fetch(`${METAPHYSICS_URL}?query=${encodeURIComponent(query)}`, { 98 | headers: headerAuthParams(user) 99 | }) 100 | .then(checkStatus) 101 | .then(parseJSON) 102 | } 103 | 104 | /** 105 | * Runs a query against gravity, e.g. favouriting an Artwork 106 | * 107 | * @export 108 | * @param {any} body 109 | * @param {APIToken} apiToken 110 | * @returns {Promise} 111 | */ 112 | export async function gravityPost(body: any = {}, path: string, user: MitosisUser): Promise { 113 | await ensureAuthenticationCredentials(user) 114 | return fetch(`${GRAVITY_URL}${path}`, { 115 | method: "POST", 116 | body: JSON.stringify(body), 117 | headers: { "Content-Type": "application/json", ...headerAuthParams(user) } 118 | }) 119 | .then(checkStatus) 120 | .then(parseJSON) 121 | } 122 | 123 | /** 124 | * Gravity get request 125 | * 126 | * @export 127 | * @param {any} body 128 | * @param {APIToken} apiToken 129 | * @returns {Promise} 130 | */ 131 | export async function gravity(path: string, user: MitosisUser): Promise { 132 | await ensureAuthenticationCredentials(user) 133 | return fetch(`${GRAVITY_URL}${path}`, { 134 | headers: { "Content-Type": "application/json", ...headerAuthParams(user) } 135 | }) 136 | .then(checkStatus) 137 | .then(parseJSON) 138 | } 139 | 140 | // Are all the credentials good to go in the DB? 141 | // Looks inside the JWT for the expiration dates. 142 | function isAuthenticationIsUpToDate(user: MitosisUser): bool { 143 | if (user.artsyOauthAppCode && user.userToken === undefined) { 144 | // Got a code, but not got user token 145 | return false 146 | } else if (user.artsyOauthAppCode && user.userToken) { 147 | // verify expiration date in JWT for user token 148 | const options = jwt.decode(user.userToken) 149 | const now = Math.floor(new Date() / 1000) 150 | return options.exp > now 151 | } else if (user.xappToken) { 152 | // verify expiration date in JWT for xapp 153 | const options = jwt.decode(user.xappToken) 154 | const now = Math.floor(new Date() / 1000) 155 | return options.exp > now 156 | } else { 157 | // Not set up yet 158 | return false 159 | } 160 | } 161 | 162 | // This is duped from the fb stuff 163 | function checkStatus(response: any) { 164 | if (response.status >= 200 && response.status < 300) { 165 | return response 166 | } else { 167 | response.json().then(json => { 168 | console.error("\n\nResponse from Gravity error:") 169 | console.error(JSON.stringify(json)) 170 | }) 171 | var error = new NetworkError(response.statusText) 172 | error.response = response 173 | throw error 174 | } 175 | } 176 | 177 | function parseJSON(response: any) { 178 | return response.json() 179 | } 180 | 181 | /** 182 | * An Error type with an attached network response 183 | * 184 | * @extends {Error} 185 | */ 186 | export class NetworkError extends Error { 187 | response: any 188 | } 189 | -------------------------------------------------------------------------------- /source/bot/contexts/article/element.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { GenericElement } from "../../../facebook/types" 4 | import { WEB_URL } from "../../../globals" 5 | 6 | export function elementForArticle(article: any): GenericElement { 7 | const url = WEB_URL + article.href 8 | return { 9 | title: article.thumbnail_title, 10 | item_url: url, 11 | image_url: article.thumbnail_image.url, 12 | buttons: [{ 13 | type: "web_url", 14 | url: url, 15 | title: "Open on Artsy" 16 | }] 17 | } 18 | } 19 | 20 | export const elementArticleEssentialsGraphQL = ` 21 | href 22 | thumbnail_title 23 | href 24 | thumbnail_image { 25 | url 26 | } 27 | ` 28 | -------------------------------------------------------------------------------- /source/bot/contexts/article/queries.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { elementArticleEssentialsGraphQL } from "./element" 4 | 5 | export const newArticlesQuery = () => ` 6 | { 7 | articles(sort: PUBLISHED_AT_DESC, published:true) { 8 | ${elementArticleEssentialsGraphQL} 9 | published_at 10 | } 11 | } 12 | ` 13 | -------------------------------------------------------------------------------- /source/bot/contexts/artist.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../../facebook/api" 4 | import { metaphysicsQuery } from "../artsy-api" 5 | import { elementForArtwork } from "./artwork/element" 6 | import { elementForArticle } from "./article/element" 7 | import type { MitosisUser } from "../types" 8 | import { artistArtworksQuery, artistQuery, artistArticlesQuery } from "./artist/queries" 9 | 10 | // Keys for callback resolution 11 | 12 | export const ArtistShowArtworksKey = "artist-artworks-show" 13 | export const ArtistOverviewKey = "artist-overview" 14 | export const ArtistShowKey = "artist-show" 15 | export const ArtistFavouriteKey = "artist-favourite" 16 | export const ArtistArticlesKey = "artist-articles" 17 | 18 | /** 19 | * Handles pulling out the payload keys and running the appropriate function 20 | * 21 | * @export 22 | * @param {MitosisUser} context the user details 23 | * @param {string} payload a string for the lookup 24 | */ 25 | export function handleArtistCallbacks(context: MitosisUser, payload: string) { 26 | if (payload.startsWith(ArtistOverviewKey)) { callbackForArtistOverview(context, payload) } 27 | if (payload.startsWith(ArtistShowArtworksKey)) { callbackForArtistArtworks(context, payload) } 28 | if (payload.startsWith(ArtistShowKey)) { callbackForShowingArtist(context, payload) } 29 | if (payload.startsWith(ArtistFavouriteKey)) { callbackForSavingArtist(context, payload) } 30 | if (payload.startsWith(ArtistArticlesKey)) { callbackForArticles(context, payload) } 31 | } 32 | 33 | // General overview, show a few artworks too 34 | async function callbackForArtistOverview(context: MitosisUser, payload: string) { 35 | const [, artistID, artistName] = payload.split("::") 36 | const artistIDAndName = artistID + "::" + artistName 37 | 38 | fbapi.startTyping(context.fbSenderID) 39 | const results = await metaphysicsQuery(artistQuery(artistID), context) 40 | const hasArticles = results.data.artist.articles.length > 0 41 | 42 | const elements = results.data.artist.artworks.map(a => elementForArtwork(a)) 43 | await fbapi.elementCarousel(context.fbSenderID, `Artworks for ${artistName}`, elements, [ 44 | { content_type: "text", title: "Favourite Artist", payload: `${ArtistFavouriteKey}::${artistIDAndName}` }, 45 | { content_type: "text", title: `More About ${artistName}`, payload: `${ArtistShowKey}::${artistIDAndName}` }, 46 | { content_type: "text", title: "Show More Artworks", payload: `${ArtistShowArtworksKey}::${artistIDAndName}::2` }, 47 | hasArticles ? { content_type: "text", title: "Related Articles", payload: `${ArtistArticlesKey}::${artistIDAndName}` } : null 48 | ]) 49 | } 50 | 51 | // Deeper overview, more focused on the artist metadata specifically 52 | async function callbackForShowingArtist(context: MitosisUser, payload: string) { 53 | const [, artistID, artistName] = payload.split("::") 54 | const artistIDAndName = artistID + "::" + artistName 55 | 56 | fbapi.startTyping(context.fbSenderID) 57 | const results = await metaphysicsQuery(artistQuery(artistID), context) 58 | const artist = results.data.artist 59 | fbapi.sendLongMessage(context.fbSenderID, ` 60 | > ${artist.name} 61 | ${artist.formatted_nationality_and_birthday} 62 | ${artist.formatted_artworks_count} 63 | 64 | ${artist.bio} 65 | 66 | ${artist.blurb || ""}`) 67 | await fbapi.quickReply(context.fbSenderID, `${artistName}`, [ 68 | { content_type: "text", title: "Favourite", payload: `${ArtistFavouriteKey}::${artistIDAndName}` }, 69 | { content_type: "text", title: "More Artworks", payload: `${ArtistShowArtworksKey}::${artistIDAndName}::2` } 70 | ]) 71 | } 72 | 73 | // Show a few Artworks in a carousel 74 | async function callbackForArtistArtworks(context: MitosisUser, payload: string) { 75 | const [, artistID, artistName, pageNumberString] = payload.split("::") 76 | const artistIDAndName = artistID + "::" + artistName 77 | const pageNumber = parseInt(pageNumberString) 78 | 79 | fbapi.startTyping(context.fbSenderID) 80 | const results = await metaphysicsQuery(artistArtworksQuery(artistID, pageNumber), context) 81 | 82 | await fbapi.elementCarousel(context.fbSenderID, `Artworks at page ${pageNumber} for ${artistName}`, results.data.artist.artworks.map(a => elementForArtwork(a)), [ 83 | { content_type: "text", title: "More Artworks", payload: `${ArtistShowArtworksKey}::${artistIDAndName}::${pageNumber + 1}` }, 84 | { content_type: "text", title: `About ${artistName}`, payload: `${ArtistOverviewKey}::${artistIDAndName}` } 85 | ]) 86 | } 87 | 88 | // Saves an artwork to your user account, will need to handle forcing you to log in too 89 | async function callbackForSavingArtist(context: MitosisUser, payload: string) { 90 | // const [, artistID] = payload.split("::") 91 | // await gravityPost({ artist_id: artistID }, "/api/v1/me/follow/artist", context) 92 | fbapi.sendTextMessage(context.fbSenderID, "Sorry - Artsy login isn't working yet") 93 | } 94 | 95 | // Shows related articles to an artist 96 | async function callbackForArticles(context: MitosisUser, payload: string) { 97 | const [, artistID, artistName] = payload.split("::") 98 | const artistIDAndName = artistID + "::" + artistName 99 | 100 | fbapi.startTyping(context.fbSenderID) 101 | const results = await metaphysicsQuery(artistArticlesQuery(artistID), context) 102 | const elements = results.data.artist.articles.map(a => elementForArticle(a)) 103 | await fbapi.elementCarousel(context.fbSenderID, `Articles about ${artistName}`, elements, [ 104 | { content_type: "text", title: "More Artworks", payload: `${ArtistShowArtworksKey}::${artistIDAndName}::2` }, 105 | { content_type: "text", title: `About ${artistName}`, payload: `${ArtistOverviewKey}::${artistIDAndName}` } 106 | ]) 107 | } 108 | -------------------------------------------------------------------------------- /source/bot/contexts/artist/element.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { ArtistFavouriteKey, ArtistOverviewKey } from "../artist" 4 | import type { GenericElement } from "../../../facebook/types" 5 | import { WEB_URL } from "../../../globals" 6 | 7 | export function elementForMetaphysicsArtist(artist: any): GenericElement { 8 | const url = WEB_URL + artist.href 9 | return { 10 | title: artist.name, 11 | subtitle: artist.blurb, 12 | item_url: url, 13 | image_url: artist.image.url, 14 | buttons: [{ 15 | type: "postback", 16 | title: "Show More Info", 17 | payload: `${ArtistOverviewKey}::${artist.id}::${artist.name}` 18 | }, { 19 | type: "postback", 20 | title: "Follow", 21 | payload: `${ArtistFavouriteKey}::${artist.id}::${artist.name}` 22 | }] 23 | } 24 | } 25 | 26 | export const elementArtistEssentialsGraphQL = ` 27 | id 28 | name 29 | blurb 30 | href 31 | image { 32 | url 33 | } 34 | ` 35 | 36 | export function elementForGravityArtist(artist: any): GenericElement { 37 | const url = WEB_URL + "/artist/" + artist.id 38 | return { 39 | title: artist.name, 40 | subtitle: artist.nationality, 41 | item_url: url, 42 | image_url: artist.image_urls.four_thirds, 43 | buttons: [{ 44 | type: "postback", 45 | title: "Show More Info", 46 | payload: `${ArtistOverviewKey}::${artist.id}::${artist.name}` 47 | }, { 48 | type: "postback", 49 | title: "Follow", 50 | payload: `${ArtistFavouriteKey}::${artist.id}::${artist.name}` 51 | }] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/bot/contexts/artist/queries.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { elementArtistEssentialsGraphQL } from "./element" 4 | import { elementArticleEssentialsGraphQL } from "../article/element" 5 | 6 | export const artistArtworksQuery = (artistID: string, page: number) => ` 7 | { 8 | artist(id:"${artistID}") { 9 | id 10 | name 11 | artworks(size: 5, sort: iconicity_desc, published: true, page:${page}) { 12 | id 13 | title 14 | description 15 | href 16 | images { 17 | url 18 | } 19 | } 20 | } 21 | } 22 | ` 23 | 24 | export const artistQuery = (artistID: string) => ` 25 | { 26 | artist(id:"${artistID}") { 27 | ${elementArtistEssentialsGraphQL} 28 | bio 29 | blurb 30 | formatted_nationality_and_birthday 31 | formatted_artworks_count 32 | shows(active: true) { 33 | id 34 | is_displayable 35 | is_active 36 | name 37 | } 38 | artworks(size: 5, sort: iconicity_desc, published: true) { 39 | id 40 | title 41 | description 42 | href 43 | images { 44 | url 45 | } 46 | } 47 | articles(limit: 1) { 48 | ${elementArticleEssentialsGraphQL} 49 | } 50 | artists { 51 | name 52 | } 53 | } 54 | } 55 | ` 56 | 57 | export const artistArticlesQuery = (artistID: string) => ` 58 | { 59 | artist(id:"${artistID}") { 60 | id 61 | name 62 | articles(limit: 10) { 63 | ${elementArticleEssentialsGraphQL} 64 | } 65 | } 66 | } 67 | ` 68 | -------------------------------------------------------------------------------- /source/bot/contexts/artwork.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../../facebook/api" 4 | import type { MitosisUser } from "../types" 5 | import { metaphysicsQuery } from "../artsy-api" 6 | import { elementForArtwork } from "./artwork/element" 7 | import { elementForArticle } from "./article/element" 8 | import { artworkQuery, artworkRelatedArticlesQuery, artworkRelatedArtworksQuery } from "./artwork/queries" 9 | import { ArtistOverviewKey, ArtistFavouriteKey } from "./artist" 10 | import { SerendipityNewArticles } from "./serendipity" 11 | 12 | // Keys for callback resolution 13 | 14 | export const ArtworkFavouriteKey = "artwork-favourite" 15 | export const ArtworkOverviewKey = "artwork-overview" 16 | export const ArtworkShowKey = "artwork-show" 17 | export const ArtworkRelatedArtworksKey = "artwork-related-artworks" 18 | export const ArtworkRelatedArticlesKey = "artwork-related-articles" 19 | 20 | /** 21 | * Handles pulling out the payload keys and running the appropriate function 22 | * 23 | * @export 24 | * @param {MitosisUser} context the user details 25 | * @param {string} payload a string for the lookup 26 | */ 27 | 28 | export function handleArtworkCallbacks(context: MitosisUser, payload: string) { 29 | if (payload.startsWith(ArtworkFavouriteKey)) { callbackForFavouritingArtwork(context, payload) } 30 | if (payload.startsWith(ArtworkOverviewKey)) { callbackForArtworkOverview(context, payload) } 31 | if (payload.startsWith(ArtworkRelatedArtworksKey)) { callbackForArtworkRelatedArtworks(context, payload) } 32 | if (payload.startsWith(ArtworkRelatedArticlesKey)) { callbackForArtworkRelatedArticles(context, payload) } 33 | } 34 | 35 | // Saving an Artwork 36 | async function callbackForFavouritingArtwork(context: MitosisUser, payload: string) { 37 | const [, artworkID, artworkName] = payload.split("::") 38 | 39 | // Save it 40 | fbapi.startTyping(context.fbSenderID) 41 | // await gravityPost({ user_id: context.artsyUserID }, `/api/v1/collection/saved-artwork/artwork/${artworkID}`, context) 42 | await fbapi.sendTextMessage(context.fbSenderID, "Sorry - Artsy login isn't working yet") 43 | 44 | // Offer some jump-off places 45 | const result = await metaphysicsQuery(artworkQuery(artworkID), context) 46 | const artist = result.data.artist[0] 47 | const artistIDAndName = `${artist.id}::${artist.name}` 48 | 49 | await fbapi.quickReply(context.fbSenderID, `Saved, ${artworkName} to your Favourites`, [ 50 | { content_type: "text", title: "Favourite Artist", payload: `${ArtistFavouriteKey}::${artistIDAndName}` }, 51 | { content_type: "text", title: `About ${artist.name}`, payload: `${ArtistOverviewKey}::${artistIDAndName}` } 52 | // { content_type: "text", title: "More from Expressionism", payload: `gene-show::${geneIDAndName}` } 53 | ]) 54 | await fbapi.stopTyping(context.fbSenderID) 55 | } 56 | 57 | // General overview of an Artwork, e.g. description etc 58 | async function callbackForArtworkOverview(context: MitosisUser, payload: string): ?Promise { 59 | const [, artworkID] = payload.split("::") 60 | 61 | const result = await metaphysicsQuery(artworkQuery(artworkID), context) 62 | const artwork = result.data.artwork 63 | const artist = artwork.artists[0] 64 | const artworkIDAndName = `${artwork.id}::${artwork.title}` 65 | const artistIDAndName = `${artist.id}::${artist.name}` 66 | const hasRelatedArtworks = artwork.related.length > 0 67 | const hasRelatedArticles = artwork.related.length > 0 68 | 69 | // Show the Artist image + link 70 | await fbapi.elementCarousel(context.fbSenderID, `About Artwork ${artwork.title}`, [elementForArtwork(artwork)], []) 71 | 72 | // Try show some useful info about the work, or the artist 73 | let message = "Find out more about this work" 74 | if (artwork.description !== null) { 75 | message = artwork.description 76 | } else if (artist.blurb !== null) { 77 | message = "About the Artist:\n\n" + artist.blurb 78 | } 79 | 80 | // Offer some jump off points 81 | await fbapi.quickReply(context.fbSenderID, message, [ 82 | { content_type: "text", title: "Add to Favourite", payload: `${ArtistFavouriteKey}::${artistIDAndName}` }, 83 | { content_type: "text", title: `About ${artist.name}`, payload: `${ArtistOverviewKey}::${artistIDAndName}` }, 84 | hasRelatedArtworks ? { content_type: "text", title: "Related Artworks", payload: `${ArtworkRelatedArtworksKey}::${artworkIDAndName}` } : null, 85 | hasRelatedArticles ? { content_type: "text", title: "Related Articles", payload: `${ArtworkRelatedArticlesKey}::${artworkIDAndName}` } : null 86 | ]) 87 | } 88 | 89 | // Shows artworks related to an Artwork 90 | async function callbackForArtworkRelatedArtworks(context: MitosisUser, payload: string): ?Promise { 91 | const [, artworkID, artworkName] = payload.split("::") 92 | 93 | const result = await metaphysicsQuery(artworkRelatedArtworksQuery(artworkID), context) 94 | const artworks = result.data.artwork.related 95 | 96 | await fbapi.elementCarousel(context.fbSenderID, `Artworks Related to ${artworkName}`, artworks.map((a) => elementForArtwork(a)), []) 97 | } 98 | 99 | // Shows related articles to an artist 100 | async function callbackForArtworkRelatedArticles(context: MitosisUser, payload: string) { 101 | const [, artworkID, artworkName] = payload.split("::") 102 | 103 | fbapi.startTyping(context.fbSenderID) 104 | const results = await metaphysicsQuery(artworkRelatedArticlesQuery(artworkID), context) 105 | await fbapi.elementCarousel(context.fbSenderID, `Articles Related to ${artworkName}`, results.data.artwork.articles.map(a => elementForArticle(a)), [ 106 | { content_type: "text", title: "New Articles", payload: SerendipityNewArticles } 107 | ]) 108 | } 109 | -------------------------------------------------------------------------------- /source/bot/contexts/artwork/element.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { ArtworkFavouriteKey, ArtworkOverviewKey } from "../artwork" 4 | import type { GenericElement } from "../../../facebook/types" 5 | import { WEB_URL } from "../../../globals" 6 | 7 | export function elementForArtwork(artwork: any): GenericElement { 8 | const url = WEB_URL + artwork.href 9 | return { 10 | title: artwork.title || "Untitled", 11 | subtitle: artwork.description, 12 | item_url: url, 13 | image_url: artwork.images[0].url, 14 | buttons: [{ 15 | type: "postback", 16 | title: "Favourite", 17 | payload: `${ArtworkFavouriteKey}::${artwork.id}::${artwork.title}` 18 | }, { 19 | type: "postback", 20 | title: "More info", 21 | payload: `${ArtworkOverviewKey}::${artwork.id}::${artwork.title}` 22 | }] 23 | } 24 | } 25 | 26 | export const elementArtworkEssentialsGraphQL = ` 27 | id 28 | title 29 | description 30 | href 31 | images { 32 | url 33 | } 34 | ` 35 | -------------------------------------------------------------------------------- /source/bot/contexts/artwork/queries.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const artworkQuery = (artworkID: string) => ` 4 | { 5 | artwork(id: "${artworkID}"){ 6 | id 7 | title 8 | description 9 | href 10 | images { 11 | url 12 | } 13 | artists { 14 | id 15 | name 16 | blurb 17 | } 18 | articles(size:1) { 19 | id 20 | } 21 | related(size:1) { 22 | id 23 | } 24 | } 25 | } 26 | ` 27 | 28 | export const artworkRelatedArticlesQuery = (artworkID: string) => ` 29 | { 30 | artwork(id: "${artworkID}"){ 31 | id 32 | title 33 | href 34 | articles(size:5) { 35 | title, 36 | href 37 | thumbnail_image { 38 | url 39 | } 40 | } 41 | } 42 | } 43 | ` 44 | 45 | export const artworkRelatedArtworksQuery = (artworkID: string) => ` 46 | { 47 | artwork(id: "${artworkID}"){ 48 | id 49 | title 50 | href 51 | related(size:5) { 52 | id 53 | title 54 | description 55 | href 56 | images { 57 | url 58 | } 59 | } 60 | } 61 | } 62 | ` 63 | -------------------------------------------------------------------------------- /source/bot/contexts/main-menu.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../../facebook/api" 4 | import { SettingsShowKey } from "./settings" 5 | import { ShowsNearMeKey } from "./shows" 6 | import type { MitosisUser } from "../types" 7 | 8 | export const MainMenuKey = "main-menu-show" 9 | 10 | import { SerendipityTrendingArtists, SerendipityNewArticles } from "./serendipity" 11 | 12 | /** 13 | * Handles pulling out the payload keys and running the appropriate function 14 | * 15 | * @export 16 | * @param {MitosisUser} context the user details 17 | * @param {string} payload a string for the lookup 18 | */ 19 | export function handleMenuCallbacks(context: MitosisUser, payload: string) { 20 | if (payload.startsWith(MainMenuKey)) { callbackForMainMenu(context, payload) } 21 | } 22 | 23 | // General overview, show a few artworks too 24 | async function callbackForMainMenu(context: MitosisUser, payload: string) { 25 | // Maybe show trending artists once a week? 26 | callbackForMainMenu(context, "Would you like to look around?") 27 | } 28 | 29 | export async function showMainMenu(context: MitosisUser, message: string) { 30 | await fbapi.quickReply(context.fbSenderID, message, [ 31 | { content_type: "text", title: "New Articles", payload: SerendipityNewArticles }, 32 | { content_type: "text", title: "Shows Nearby", payload: ShowsNearMeKey }, 33 | { content_type: "text", title: "Trending Artists", payload: SerendipityTrendingArtists }, 34 | { content_type: "text", title: "Emerging Artists", payload: SerendipityTrendingArtists }, 35 | { content_type: "text", title: "Settings", payload: SettingsShowKey } 36 | ]) 37 | } 38 | -------------------------------------------------------------------------------- /source/bot/contexts/serendipity.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../../facebook/api" 4 | import type { MitosisUser } from "../types" 5 | import { metaphysicsQuery, gravity, GravityTrendingArtistsAPI, GravityEmergingArtistsAPI } from "../artsy-api" 6 | import { elementForArticle } from "./article/element" 7 | import { elementForGravityArtist } from "./artist/element" 8 | import { newArticlesQuery } from "./article/queries" 9 | 10 | // Keys for callback resolution 11 | 12 | export const SerendipityTrendingArtists = "serendipity-trending-artists" 13 | export const SerendipityEmergingArtists = "serendipity-emerging-artists" 14 | export const SerendipityNewArticles = "serendipity-new-articles" 15 | 16 | /** 17 | * Handles pulling out the payload keys and running the appropriate function 18 | * 19 | * @export 20 | * @param {MitosisUser} context the user details 21 | * @param {string} payload a string for the lookup 22 | */ 23 | 24 | export function handleSerendipityCallbacks(context: MitosisUser, payload: string) { 25 | if (payload.startsWith(SerendipityTrendingArtists)) { callbackTrendingArtists(context, payload) } 26 | if (payload.startsWith(SerendipityEmergingArtists)) { callbackEmergingArtists(context, payload) } 27 | if (payload.startsWith(SerendipityNewArticles)) { callbackNewArticles(context, payload) } 28 | } 29 | 30 | // Shows trending artists 31 | async function callbackTrendingArtists(context: MitosisUser, payload: string) { 32 | fbapi.startTyping(context.fbSenderID) 33 | const artists = await gravity(GravityTrendingArtistsAPI, context) 34 | fbapi.elementCarousel(context.fbSenderID, "Trending Artists", artists.map((a) => elementForGravityArtist(a)), []) 35 | } 36 | 37 | // Shows emerging artists 38 | async function callbackEmergingArtists(context: MitosisUser, payload: string) { 39 | fbapi.startTyping(context.fbSenderID) 40 | const artists = await gravity(GravityEmergingArtistsAPI, context) 41 | fbapi.elementCarousel(context.fbSenderID, "Trending Artists", artists.map((a) => elementForGravityArtist(a)), []) 42 | } 43 | 44 | // Shows new articles 45 | async function callbackNewArticles(context: MitosisUser, payload: string) { 46 | fbapi.startTyping(context.fbSenderID) 47 | const results = await metaphysicsQuery(newArticlesQuery(), context) 48 | const articles = results.data.articles 49 | await fbapi.elementCarousel(context.fbSenderID, "New Articles on Artsy", articles.map(a => elementForArticle(a)), []) 50 | } 51 | -------------------------------------------------------------------------------- /source/bot/contexts/serendipity/queries.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { elementArtistEssentialsGraphQL } from "../artist/element" 3 | 4 | export const trendingArtistsQuery = () => { 5 | const sorts = ["ARTIST_SEARCH", "ARTIST_SAVE", "ARTIST_FAIR"] 6 | const randomIndex = Math.floor(Math.random() * sorts.length) 7 | return ` 8 | { 9 | trending_artists(name:${sorts[randomIndex]}) { 10 | artists { 11 | ${elementArtistEssentialsGraphQL} 12 | } 13 | } 14 | } 15 | ` 16 | } 17 | 18 | -------------------------------------------------------------------------------- /source/bot/contexts/settings.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../../facebook/api" 4 | import { updateMitosisUser } from "../../db/mongo" 5 | import type { MitosisUser } from "../types" 6 | import { showMainMenu } from "./main-menu" 7 | 8 | export const SettingsShowKey = "settings-show" 9 | export const SettingsLoginKey = "settings-login" 10 | export const SettingsLogoutKey = "settings-logout" 11 | export const SettingsArticleSubscriptionKey = "settings-subscription-toggle" 12 | export const SettingsArticleSubscriptionStartKey = "settings-subscription-update" 13 | export const SettingsArticleSubscriptionUpdateKey = "settings-subscription-start" 14 | 15 | /** 16 | * Handles pulling out the payload keys and running the appropriate function 17 | * 18 | * @export 19 | * @param {MitosisUser} context the user details 20 | * @param {string} payload a string for the lookup 21 | */ 22 | export function handleSettingsCallbacks(context: MitosisUser, payload: string) { 23 | if (payload.startsWith(SettingsShowKey)) { callbackForSettingsShow(context, payload) } 24 | if (payload.startsWith(SettingsArticleSubscriptionKey)) { callbackForSettingsArticleSubscriptionToggle(context, payload) } 25 | if (payload.startsWith(SettingsArticleSubscriptionStartKey)) { callbackForSettingsArticleSubscriptionStart(context, payload) } 26 | if (payload.startsWith(SettingsArticleSubscriptionUpdateKey)) { callbackForSettingsArticleSubscriptionUpdateToggle(context, payload) } 27 | if (payload.startsWith(SettingsLoginKey)) { callbackForLogin(context, payload) } 28 | if (payload.startsWith(SettingsLogoutKey)) { callbackForLogout(context, payload) } 29 | } 30 | 31 | /** 32 | * Shows the potential times for a user to sign up, my wife was annoyed at only 33 | * having morning options, so now we also show evening options. 34 | * 35 | * @param {MitosisUser} context 36 | */ 37 | function showTimes(context: MitosisUser) { 38 | fbapi.quickReply(context.fbSenderID, "What time is good for you?", [ 39 | { content_type: "text", title: "6am", payload: `${SettingsArticleSubscriptionStartKey}::6::6am` }, 40 | { content_type: "text", title: "7am", payload: `${SettingsArticleSubscriptionStartKey}::7::7am` }, 41 | { content_type: "text", title: "8am", payload: `${SettingsArticleSubscriptionStartKey}::8::8am` }, 42 | { content_type: "text", title: "6pm", payload: `${SettingsArticleSubscriptionStartKey}::18::6pm` }, 43 | { content_type: "text", title: "7pm", payload: `${SettingsArticleSubscriptionStartKey}::19::7pm` } 44 | ]) 45 | } 46 | 47 | // Shows the Settings overview 48 | async function callbackForSettingsShow(context: MitosisUser, payload: string) { 49 | const toggleString = context.subscribeToArticlesBiDaily ? "Stop Daily Articles" : "Get Daily Articles" 50 | const subscribed = context.subscribeToArticlesBiDaily ? "subscribed" : "not subscribed" 51 | const loggedIn = context.artsyOauthAppCode !== undefined 52 | const artsyLoggedIn = loggedIn ? "logged in" : "not logged in" 53 | const artsyLoggedInTitle = loggedIn ? "Log Out" : "Log In" 54 | const artsyLoggedInPayload = loggedIn ? SettingsLogoutKey : SettingsLoginKey 55 | 56 | await fbapi.quickReply(context.fbSenderID, `You currently ${subscribed} for Article updates, and are ${artsyLoggedIn} to Artsy.`, [ 57 | { content_type: "text", title: toggleString, payload: SettingsArticleSubscriptionKey }, 58 | { content_type: "text", title: artsyLoggedInTitle, payload: artsyLoggedInPayload }, 59 | context.subscribeToArticlesBiDaily ? { content_type: "text", title: "Change time", payload: SettingsArticleSubscriptionUpdateKey } : null 60 | ]) 61 | } 62 | 63 | // Shows the Settings overview 64 | async function callbackForLogin(context: MitosisUser, payload: string) { 65 | fbapi.showLoginScreen(context.fbSenderID) 66 | } 67 | 68 | async function callbackForLogout(context: MitosisUser, payload: string) { 69 | fbapi.showLogout(context.fbSenderID) 70 | } 71 | 72 | // Toggle subscription on / off. For on - it will delegate work to callbackForSettingsArticleSubscriptionStart 73 | async function callbackForSettingsArticleSubscriptionToggle(context: MitosisUser, payload: string) { 74 | if (context.subscribeToArticlesBiDaily) { 75 | context.subscribeToArticlesBiDaily = false 76 | await updateMitosisUser(context) 77 | fbapi.sendTextMessage(context.fbSenderID, "Unsubscribed") 78 | } else { 79 | showTimes(context) 80 | } 81 | } 82 | 83 | // Only update the times 84 | async function callbackForSettingsArticleSubscriptionUpdateToggle(context: MitosisUser, payload: string) { 85 | showTimes(context) 86 | } 87 | 88 | // Start a new subscription with a time, then show the main menu after confirming 89 | async function callbackForSettingsArticleSubscriptionStart(context: MitosisUser, payload: string) { 90 | const [, hourString, hourPretty] = payload.split("::") 91 | const hour = parseInt(hourString) 92 | const fbData = await fbapi.getFBUserDetails(context.fbSenderID) 93 | const gmtHour = hour + fbData.timezone 94 | 95 | // Update the local store for this user 96 | context.renderedGMTTimeForArticles = gmtHour 97 | context.subscribeToArticlesBiDaily = true 98 | await updateMitosisUser(context) 99 | 100 | const congratsAndMainMenu = `Done, you will start recieving messages at ${hourPretty}.\n\nThose will start from tomorrow, in the mean time you are welcome to explore Artsy from here, you can start with these:` 101 | showMainMenu(context, congratsAndMainMenu) 102 | } 103 | -------------------------------------------------------------------------------- /source/bot/contexts/shows.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../../facebook/api" 4 | import type { MitosisUser } from "../types" 5 | import { Cities } from "places" 6 | import { updateMitosisUser } from "../../db/mongo" 7 | import { GravityShowsSearchAPI, gravity, metaphysicsQuery } from "../artsy-api" 8 | import { elementForArtwork } from "./artwork/element" 9 | import { elementForGravityShow } from "./shows/element" 10 | import { showInfoQuery } from "./shows/queries" 11 | const querystring = require("querystring") 12 | 13 | interface City { 14 | slug: string, 15 | name: string, 16 | coords: number[], 17 | sort_order: number, 18 | timezone: number 19 | } 20 | 21 | // Keys for callback resolution 22 | 23 | export const ShowsNearMeKey = "shows-near-me" 24 | export const ShowsSaveAsMyCity = "shows-save-for-my-city" 25 | export const ShowsSetMyCity = "shows-set-for-my-city" 26 | export const ShowsForCityKey = "shows-for-city" 27 | export const ShowsShowKey = "shows-show" 28 | export const ShowsShowInfo = "shows-info-show" 29 | export const ShowsShowPressRelease = "shows-press-release-show" 30 | export const ShowsShowArtworks = "shows-artworks-show" 31 | export const ShowsFavPartner = "shows-favourite-partner" 32 | export const ShowsInferCity = "shows-infer-city" 33 | 34 | /** 35 | * Handles pulling out the payload keys and running the appropriate function 36 | * 37 | * @export 38 | * @param {MitosisUser} context the user details 39 | * @param {string} payload a string for the lookup 40 | */ 41 | 42 | export function handleShowsCallbacks(context: MitosisUser, payload: string) { 43 | if (payload.startsWith(ShowsNearMeKey)) { callbackShowsNearMe(context, payload) } 44 | if (payload.startsWith(ShowsForCityKey)) { callbackShowsForCity(context, payload) } 45 | if (payload.startsWith(ShowsSaveAsMyCity)) { callbackForShowsSaveAsMyCity(context, payload) } 46 | if (payload.startsWith(ShowsSetMyCity)) { callbackForSetSaveAsMyCity(context, payload) } 47 | if (payload.startsWith(ShowsShowInfo)) { callbackForShowsInfo(context, payload) } 48 | if (payload.startsWith(ShowsShowPressRelease)) { callbackForShowsPressRelease(context, payload) } 49 | if (payload.startsWith(ShowsInferCity)) { callbackForShowsInferCity(context, payload) } 50 | } 51 | 52 | // Shows a list of shows nearby, or just jumps straight into shows nearby 53 | async function callbackShowsNearMe(context: MitosisUser, payload: string) { 54 | fbapi.startTyping(context.fbSenderID) 55 | const cities = citiesForUser(context) 56 | if (cities.length === 1) { 57 | // If there's only going be one result, skip showing an option 58 | callbackShowsForCity(context, `${ShowsForCityKey}::${cities.pop().slug}`) 59 | return 60 | } 61 | // Present a list to choose from 62 | await fbapi.quickReply(context.fbSenderID, "Which is the closest city to you?", cities.map((city) => { 63 | return { content_type: "text", title: city.name, payload: `${ShowsSaveAsMyCity}::${city.slug}::${city.name}` } 64 | })) 65 | } 66 | 67 | // Highlight shows for a city 68 | async function callbackShowsForCity(context: MitosisUser, payload: string) { 69 | const [, showCityID] = payload.split("::") 70 | const city = Cities.find((c) => c.slug === showCityID) 71 | const query = querystring.stringify({ 72 | near: city.coords.toString(), 73 | sort: "-start_at", 74 | size: 5, 75 | displayable: true, 76 | at_a_fair: false 77 | }) 78 | 79 | const url = `${GravityShowsSearchAPI}?${query}` 80 | const shows = await gravity(url, context) 81 | await fbapi.elementCarousel(context.fbSenderID, `Shows near ${city.name}`, shows.map(show => elementForGravityShow(show)), []) 82 | } 83 | 84 | // A show overview, showing artworks, and supports paginating through the works 85 | async function callbackForShowsInfo(context: MitosisUser, payload: string) { 86 | const [, showID, showName, artworksPage] = payload.split("::") 87 | const page = artworksPage || "1" 88 | const query = showInfoQuery(showID, page) 89 | const results = await metaphysicsQuery(query, context) 90 | const show = results.data.show 91 | const firstPage = page === "1" 92 | if (firstPage) { 93 | let location = show.location.display 94 | if (location === null) { 95 | location = `${show.location.address}, ${show.location.postal_code}` 96 | } 97 | await fbapi.sendTextMessage(context.fbSenderID, `${show.exhibition_period} 98 | ${location} 99 | 100 | ${show.description} 101 | `) 102 | } 103 | if (show.artworks.length) { 104 | const showPressRelease = firstPage && show.press_release !== null && show.press_release.length > 0 105 | await fbapi.elementCarousel(context.fbSenderID, `Works at ${showName}`, show.artworks.map(a => elementForArtwork(a)), [ 106 | { content_type: "text", title: "More Artworks", payload: `${ShowsShowInfo}::${showID}::${showName}::${parseInt(page) + 1}` }, 107 | showPressRelease ? { content_type: "text", title: "Press Release", payload: `${ShowsShowPressRelease}::${showID}::${showName}` } : null 108 | ]) 109 | } else { 110 | await fbapi.sendTextMessage(context.fbSenderID, "That's all of the artworks for the show.") 111 | } 112 | } 113 | 114 | // Show just the press release, they are long so don't do it on info 115 | async function callbackForShowsPressRelease(context: MitosisUser, payload: string) { 116 | const [, showID] = payload.split("::") 117 | const query = showInfoQuery(showID, "1") 118 | const results = await metaphysicsQuery(query, context) 119 | const show = results.data.show 120 | await fbapi.sendLongMessage(context.fbSenderID, show.press_release) 121 | } 122 | 123 | // If you have choosen a city, let it be the default 124 | async function callbackForShowsSaveAsMyCity(context: MitosisUser, payload: string) { 125 | const [, showCityID, cityName] = payload.split("::") 126 | 127 | await fbapi.quickReply(context.fbSenderID, `Would you like to save ${cityName} as your local city?`, [ 128 | { content_type: "text", title: "Yes please", payload: `${ShowsSetMyCity}::${showCityID}::${cityName}` }, 129 | { content_type: "text", title: "No thanks", payload: `${ShowsForCityKey}::${showCityID}::${cityName}` } 130 | ]) 131 | } 132 | 133 | // Try and figure out what city the user is after 134 | async function callbackForShowsInferCity(context: MitosisUser, payload: string) { 135 | let [, message] = payload.split("::") 136 | message = message.replace("nyc", "new york") 137 | .replace("new york city", "new york") 138 | .replace("SF", "san francisco") 139 | .replace("cupertino", "san francisco") 140 | const cityID = message.replace("shows in", "").trim().replace(" ", "-").replace(".", "").replace("?", "") 141 | const city = Cities.find((c) => c.slug === cityID) 142 | if (city) { 143 | callbackShowsForCity(context, `${ShowsForCityKey}::${city.slug}::${city.name}`) 144 | } else { 145 | const sortedCities = Cities.sort((a, b) => a.sort_order < b.sort_order).reverse() 146 | fbapi.sendLongMessage(context.fbSenderID, `Sorry, we could not find your city. Here is our list of cities to work from: 147 | 148 | ${sortedCities.map((c) => { return c.name }).join(", ")}`) 149 | } 150 | } 151 | 152 | // Save your location if you wanted to, thne show the city 153 | async function callbackForSetSaveAsMyCity(context: MitosisUser, payload: string) { 154 | const [, showCityID, cityName] = payload.split("::") 155 | context.artsyLocationCitySlug = showCityID 156 | fbapi.startTyping(context.fbSenderID) 157 | await updateMitosisUser(context) 158 | fbapi.sendTextMessage(context.fbSenderID, `Set ${cityName} as your local city. You can say "shows" at any time to see shows in ${cityName}`) 159 | callbackShowsForCity(context, `${ShowsForCityKey}::${showCityID}::${cityName}`) 160 | } 161 | 162 | // Try and figure out the useful cities for a user 163 | function citiesForUser(context: MitosisUser): City[] { 164 | // Are we certain? 165 | if (context.artsyLocationCitySlug !== undefined) { 166 | const locaton = Cities.find((c) => c.slug === context.artsyLocationCitySlug) 167 | return [locaton] 168 | } 169 | 170 | // If not be pretty good about showing the first 171 | const cities = [] 172 | if (context.favouriteCitySlug !== undefined) { 173 | cities.push(Cities.find((c) => c.slug === context.favouriteCitySlug)) 174 | } 175 | 176 | // And then the rest in the timezone 177 | const citiesInTimezone = Cities.filter((c) => Math.round(c.timezone) === Math.round(context.timezoneOffset)) 178 | const sortedCities = citiesInTimezone.sort((a, b) => a.sort_order < b.sort_order).reverse() 179 | return cities.concat(sortedCities) 180 | } 181 | -------------------------------------------------------------------------------- /source/bot/contexts/shows/element.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { ShowsFavPartner, ShowsShowInfo } from "../shows" 4 | import type { GenericElement } from "../../../facebook/types" 5 | import { WEB_URL } from "../../../globals" 6 | 7 | export function elementForGravityShow(show: any): GenericElement { 8 | const url = WEB_URL + "show/" + show.id 9 | return { 10 | title: show.name, 11 | subtitle: show.partner.name, 12 | item_url: url, 13 | image_url: show.image_urls.large_rectangle || show.image_urls.featured || show.image_urls.general, 14 | buttons: [{ 15 | type: "postback", 16 | title: `Follow ${show.partner.name}`, 17 | payload: `${ShowsFavPartner}::${show.id}::${show.name}` 18 | }, { 19 | type: "postback", 20 | title: "More info", 21 | payload: `${ShowsShowInfo}::${show.id}::${show.name}::1` 22 | }] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/bot/contexts/shows/queries.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { elementArtworkEssentialsGraphQL } from "../artwork/element" 3 | 4 | export const showInfoQuery = (showID: string, artworkPage: string) => { 5 | return ` 6 | { 7 | show(id: "${showID}") { 8 | name 9 | location { 10 | id 11 | display 12 | address, 13 | postal_code 14 | } 15 | 16 | artworks(size: 5, page: ${artworkPage}) { 17 | ${elementArtworkEssentialsGraphQL} 18 | } 19 | description 20 | press_release(format: PLAIN) 21 | exhibition_period 22 | } 23 | } 24 | ` 25 | } 26 | 27 | -------------------------------------------------------------------------------- /source/bot/facebook_examples.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { api } from "../facebook/api" 4 | import { BOT_SERVER_URL } from "../globals" 5 | 6 | // If we receive a text message, check to see if it matches any special 7 | // keywords and send back the corresponding example. Otherwise, just echo 8 | // the text we received. 9 | 10 | export function exampleFallbacks(senderID: string, messageText: string) { 11 | switch (messageText) { 12 | case "image": 13 | sendImageMessage(senderID) 14 | break 15 | 16 | case "gif": 17 | sendGifMessage(senderID) 18 | break 19 | 20 | case "audio": 21 | sendAudioMessage(senderID) 22 | break 23 | 24 | case "video": 25 | sendVideoMessage(senderID) 26 | break 27 | 28 | case "file": 29 | sendFileMessage(senderID) 30 | break 31 | 32 | case "button": 33 | sendButtonMessage(senderID) 34 | break 35 | 36 | case "generic": 37 | sendGenericMessage(senderID) 38 | break 39 | 40 | case "receipt": 41 | sendReceiptMessage(senderID) 42 | break 43 | 44 | case "quick reply": 45 | sendQuickReply(senderID) 46 | break 47 | 48 | case "read receipt": 49 | sendReadReceipt(senderID) 50 | break 51 | 52 | case "typing on": 53 | sendTypingOn(senderID) 54 | break 55 | 56 | case "typing off": 57 | sendTypingOff(senderID) 58 | break 59 | 60 | case "account linking": 61 | sendAccountLinking(senderID) 62 | break 63 | } 64 | } 65 | 66 | /* 67 | * Send an image using the Send API. 68 | * 69 | */ 70 | function sendImageMessage(recipientId: string) { 71 | var messageData = { 72 | recipient: { 73 | id: recipientId 74 | }, 75 | message: { 76 | attachment: { 77 | type: "image", 78 | payload: { 79 | url: BOT_SERVER_URL + "/assets/rift.png" 80 | } 81 | } 82 | } 83 | } 84 | 85 | api(messageData) 86 | } 87 | 88 | /* 89 | * Send a Gif using the Send API. 90 | */ 91 | function sendGifMessage(recipientId: string) { 92 | var messageData = { 93 | recipient: { 94 | id: recipientId 95 | }, 96 | message: { 97 | attachment: { 98 | type: "image", 99 | payload: { 100 | url: BOT_SERVER_URL + "/assets/instagram_logo.gif" 101 | } 102 | } 103 | } 104 | } 105 | 106 | api(messageData) 107 | } 108 | 109 | /* 110 | * Send audio using the Send API. 111 | * 112 | */ 113 | function sendAudioMessage(recipientId: string) { 114 | var messageData = { 115 | recipient: { 116 | id: recipientId 117 | }, 118 | message: { 119 | attachment: { 120 | type: "audio", 121 | payload: { 122 | url: BOT_SERVER_URL + "/assets/sample.mp3" 123 | } 124 | } 125 | } 126 | } 127 | 128 | api(messageData) 129 | } 130 | 131 | /* 132 | * Send a video using the Send API. 133 | * 134 | */ 135 | function sendVideoMessage(recipientId: string) { 136 | var messageData = { 137 | recipient: { 138 | id: recipientId 139 | }, 140 | message: { 141 | attachment: { 142 | type: "video", 143 | payload: { 144 | url: BOT_SERVER_URL + "/assets/allofus480.mov" 145 | } 146 | } 147 | } 148 | } 149 | 150 | api(messageData) 151 | } 152 | 153 | /* 154 | * Send a file using the Send API. 155 | * 156 | */ 157 | function sendFileMessage(recipientId: string) { 158 | var messageData = { 159 | recipient: { 160 | id: recipientId 161 | }, 162 | message: { 163 | attachment: { 164 | type: "file", 165 | payload: { 166 | url: BOT_SERVER_URL + "/assets/test.txt" 167 | } 168 | } 169 | } 170 | } 171 | 172 | api(messageData) 173 | } 174 | 175 | /* 176 | * Send a button message using the Send API. 177 | * 178 | */ 179 | function sendButtonMessage(recipientId: string) { 180 | var messageData = { 181 | recipient: { 182 | id: recipientId 183 | }, 184 | message: { 185 | attachment: { 186 | type: "template", 187 | payload: { 188 | template_type: "button", 189 | text: "This is test text", 190 | buttons: [{ 191 | type: "web_url", 192 | url: "https://www.oculus.com/en-us/rift/", 193 | title: "Open Web URL" 194 | }, { 195 | type: "postback", 196 | title: "Trigger Postback", 197 | payload: "DEVELOPER_DEFINED_PAYLOAD" 198 | }, { 199 | type: "phone_number", 200 | title: "Call Phone Number", 201 | payload: "+16505551234" 202 | }] 203 | } 204 | } 205 | } 206 | } 207 | 208 | api(messageData) 209 | } 210 | 211 | /* 212 | * Send a Structured Message (Generic Message type) using the Send API. 213 | * 214 | */ 215 | function sendGenericMessage(recipientId: string) { 216 | var messageData = { 217 | recipient: { 218 | id: recipientId 219 | }, 220 | message: { 221 | attachment: { 222 | type: "template", 223 | payload: { 224 | template_type: "generic", 225 | elements: [{ 226 | title: "rift", 227 | subtitle: "Next-generation virtual reality", 228 | item_url: "https://www.oculus.com/en-us/rift/", 229 | image_url: BOT_SERVER_URL + "/assets/rift.png", 230 | buttons: [{ 231 | type: "web_url", 232 | url: "https://www.oculus.com/en-us/rift/", 233 | title: "Open Web URL" 234 | }, { 235 | type: "postback", 236 | title: "Call Postback", 237 | payload: "Payload for first bubble" 238 | }] 239 | }, { 240 | title: "touch", 241 | subtitle: "Your Hands, Now in VR", 242 | item_url: "https://www.oculus.com/en-us/touch/", 243 | image_url: BOT_SERVER_URL + "/assets/touch.png", 244 | buttons: [{ 245 | type: "web_url", 246 | url: "https://www.oculus.com/en-us/touch/", 247 | title: "Open Web URL" 248 | }, { 249 | type: "postback", 250 | title: "Call Postback", 251 | payload: "Payload for second bubble" 252 | }] 253 | }] 254 | } 255 | } 256 | } 257 | } 258 | 259 | api(messageData) 260 | } 261 | 262 | /* 263 | * Send a receipt message using the Send API. 264 | * 265 | */ 266 | function sendReceiptMessage(recipientId: string) { 267 | // Generate a random receipt ID as the API requires a unique ID 268 | var receiptId = "order" + Math.floor(Math.random() * 1000) 269 | 270 | var messageData = { 271 | recipient: { 272 | id: recipientId 273 | }, 274 | message: { 275 | attachment: { 276 | type: "template", 277 | payload: { 278 | template_type: "receipt", 279 | recipient_name: "Peter Chang", 280 | order_number: receiptId, 281 | currency: "USD", 282 | payment_method: "Visa 1234", 283 | timestamp: "1428444852", 284 | elements: [{ 285 | title: "Oculus Rift", 286 | subtitle: "Includes: headset, sensor, remote", 287 | quantity: 1, 288 | price: 599.00, 289 | currency: "USD", 290 | image_url: BOT_SERVER_URL + "/assets/riftsq.png" 291 | }, { 292 | title: "Samsung Gear VR", 293 | subtitle: "Frost White", 294 | quantity: 1, 295 | price: 99.99, 296 | currency: "USD", 297 | image_url: BOT_SERVER_URL + "/assets/gearvrsq.png" 298 | }], 299 | address: { 300 | street_1: "1 Hacker Way", 301 | street_2: "", 302 | city: "Menlo Park", 303 | postal_code: "94025", 304 | state: "CA", 305 | country: "US" 306 | }, 307 | summary: { 308 | subtotal: 698.99, 309 | shipping_cost: 20.00, 310 | total_tax: 57.67, 311 | total_cost: 626.66 312 | }, 313 | adjustments: [{ 314 | name: "New Customer Discount", 315 | amount: -50 316 | }, { 317 | name: "$100 Off Coupon", 318 | amount: -100 319 | }] 320 | } 321 | } 322 | } 323 | } 324 | 325 | api(messageData) 326 | } 327 | 328 | /* 329 | * Send a message with Quick Reply buttons. 330 | * 331 | */ 332 | function sendQuickReply(recipientId: string) { 333 | var messageData = { 334 | recipient: { 335 | id: recipientId 336 | }, 337 | message: { 338 | text: "What's your favorite movie genre?", 339 | quick_replies: [ 340 | { 341 | "content_type": "text", 342 | "title": "Action", 343 | "payload": "DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_ACTION" 344 | }, 345 | { 346 | "content_type": "text", 347 | "title": "Comedy", 348 | "payload": "DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_COMEDY" 349 | }, 350 | { 351 | "content_type": "text", 352 | "title": "Drama", 353 | "payload": "DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_DRAMA" 354 | } 355 | ] 356 | } 357 | } 358 | 359 | api(messageData) 360 | } 361 | 362 | /* 363 | * Send a read receipt to indicate the message has been read 364 | * 365 | */ 366 | function sendReadReceipt(recipientId: string) { 367 | console.log("Sending a read receipt to mark message as seen") 368 | 369 | var messageData = { 370 | recipient: { 371 | id: recipientId 372 | }, 373 | sender_action: "mark_seen" 374 | } 375 | 376 | api(messageData) 377 | } 378 | 379 | /* 380 | * Turn typing indicator on 381 | * 382 | */ 383 | function sendTypingOn(recipientId: string) { 384 | console.log("Turning typing indicator on") 385 | 386 | var messageData = { 387 | recipient: { 388 | id: recipientId 389 | }, 390 | sender_action: "typing_on" 391 | } 392 | 393 | api(messageData) 394 | } 395 | 396 | /* 397 | * Turn typing indicator off 398 | * 399 | */ 400 | function sendTypingOff(recipientId: string) { 401 | console.log("Turning typing indicator off") 402 | 403 | var messageData = { 404 | recipient: { 405 | id: recipientId 406 | }, 407 | sender_action: "typing_off" 408 | } 409 | 410 | api(messageData) 411 | } 412 | 413 | /* 414 | * Send a message with the account linking call-to-action 415 | * 416 | */ 417 | function sendAccountLinking(recipientId: string) { 418 | var messageData = { 419 | recipient: { 420 | id: recipientId 421 | }, 422 | message: { 423 | attachment: { 424 | type: "template", 425 | payload: { 426 | template_type: "button", 427 | text: "Welcome. Link your account.", 428 | buttons: [{ 429 | type: "account_link", 430 | 431 | url: BOT_SERVER_URL + "/authorize" 432 | }] 433 | } 434 | } 435 | } 436 | } 437 | 438 | api(messageData) 439 | } 440 | -------------------------------------------------------------------------------- /source/bot/message-parser.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { showMainMenu } from "./contexts/main-menu" 4 | import { handleSettingsCallbacks, SettingsShowKey, SettingsLogoutKey, SettingsLoginKey } from "./contexts/settings" 5 | import { handleSerendipityCallbacks, SerendipityTrendingArtists, SerendipityEmergingArtists, SerendipityNewArticles } from "./contexts/serendipity" 6 | import { handleShowsCallbacks, ShowsNearMeKey, ShowsInferCity } from "./contexts/shows" 7 | import { fbapi } from "../facebook/api" 8 | 9 | import type { MitosisUser } from "./types" 10 | 11 | /** 12 | * Handles new messages, giving us the ability to do some rudimentary responses, returns true 13 | * if it could respond to the message. 14 | * 15 | * @param {string} senderID 16 | * @param {string} payload 17 | */ 18 | export function handleUnknownMessage(context: MitosisUser, message: string, payload: string): boolean { 19 | console.log(`Recieved message: ${message}`) 20 | 21 | // Mobile users will have a capital first letter. 22 | const userMessage = message.toLowerCase().trim() 23 | 24 | if (userMessage === "help") { 25 | showMainMenu(context, "You can find shows in different cities by saying 'shows in [city]', ") 26 | return true 27 | } 28 | 29 | if (userMessage === "settings") { 30 | handleSettingsCallbacks(context, SettingsShowKey) 31 | return true 32 | } 33 | 34 | if (userMessage === "log in" || userMessage === "login") { 35 | handleSettingsCallbacks(context, SettingsLoginKey) 36 | return true 37 | } 38 | 39 | if (userMessage === "log out" || userMessage === "logout") { 40 | handleSettingsCallbacks(context, SettingsLogoutKey) 41 | return true 42 | } 43 | 44 | if (userMessage === "trending artists") { 45 | handleSerendipityCallbacks(context, SerendipityTrendingArtists) 46 | return true 47 | } 48 | 49 | if (userMessage === "emerging artists") { 50 | handleSerendipityCallbacks(context, SerendipityEmergingArtists) 51 | return true 52 | } 53 | 54 | if (userMessage === "new articles") { 55 | handleSerendipityCallbacks(context, SerendipityNewArticles) 56 | return true 57 | } 58 | 59 | if (userMessage === "shows" || userMessage === "shows nearby") { 60 | handleShowsCallbacks(context, ShowsNearMeKey) 61 | return true 62 | } 63 | 64 | if (userMessage.startsWith("shows in")) { 65 | handleShowsCallbacks(context, `${ShowsInferCity}::${userMessage}`) 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | -------------------------------------------------------------------------------- /source/bot/postback-manager.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { handleArtworkCallbacks } from "./contexts/artwork" 4 | import { handleArtistCallbacks } from "./contexts/artist" 5 | import { handleSerendipityCallbacks } from "./contexts/serendipity" 6 | import { handleSettingsCallbacks } from "./contexts/settings" 7 | import { handleMenuCallbacks } from "./contexts/main-menu" 8 | import { handleShowsCallbacks } from "./contexts/shows" 9 | 10 | import type { MitosisUser } from "./types" 11 | 12 | /** 13 | * Handles passing postbacks around the system 14 | * 15 | * @param {string} senderID 16 | * @param {string} payload 17 | */ 18 | export function handlePostbacks(context: MitosisUser, payload: string) { 19 | handleArtistCallbacks(context, payload) 20 | handleArtworkCallbacks(context, payload) 21 | handleSerendipityCallbacks(context, payload) 22 | handleSettingsCallbacks(context, payload) 23 | handleMenuCallbacks(context, payload) 24 | handleShowsCallbacks(context, payload) 25 | } 26 | -------------------------------------------------------------------------------- /source/bot/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /** Represents a GraphQL query */ 4 | export type GraphQLQuery = string 5 | /** An API Token ( or maybe a JWT in the future? ) */ 6 | export type APIToken = string 7 | 8 | /** 9 | * The fb/artsy user context for recieving/sending messages. 10 | * If you have a `userID`, there should be a `userToken`. 11 | * No functions, needs to be storable in db. 12 | */ 13 | export interface MitosisUser { 14 | /** Guest Xapp token */ 15 | xappToken?: APIToken, 16 | /** To re-auth with a user-token we need to re-use this key */ 17 | artsyOauthAppCode?: string, 18 | /** Logged in user OAuth2 token */ 19 | userToken?: APIToken, 20 | /** the Facebook chat sender ID */ 21 | fbSenderID: string, 22 | /** the corresponding Artsy User ID */ 23 | artsyUserID?: string, 24 | /** Does the user want to get articles sent every 2 days? */ 25 | subscribeToArticlesBiDaily: boolean, 26 | /** What time, in GMT should we send them articles? */ 27 | renderedGMTTimeForArticles?: number, 28 | /** Name to use _occasionally_ - I personally get annoyed if a services always uses my name */ 29 | firstName: string, 30 | /** Timezone from facebook */ 31 | timezoneOffset: number, 32 | /** last City chosen */ 33 | favouriteCitySlug?: string, 34 | /** Artsy location city */ 35 | artsyLocationCitySlug?: string 36 | } 37 | -------------------------------------------------------------------------------- /source/bot/user-setup.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { fbapi } from "../facebook/api" 4 | import { SettingsArticleSubscriptionUpdateKey } from "./contexts/settings" 5 | import { MainMenuKey } from "./contexts/main-menu" 6 | 7 | /** 8 | * Authorization Event 9 | * 10 | * The value for 'optin.ref' is defined in the entry point. For the "Send to 11 | * Messenger" plugin, it is the 'data-ref' field. Read more at 12 | * https://developers.facebook.com/docs/messenger-platform/webhook-reference/authentication 13 | * 14 | */ 15 | export async function receivedAuthentication(event: any) { 16 | var senderID = event.sender.id 17 | var recipientID = event.recipient.id 18 | var timeOfAuth = event.timestamp 19 | 20 | // The 'ref' field is set in the 'Send to Messenger' plugin, in the 'data-ref' 21 | // The developer can set this to an arbitrary value to associate the 22 | // authentication callback with the 'Send to Messenger' click event. This is 23 | // a way to do account linking when the user clicks the 'Send to Messenger' 24 | // plugin. 25 | var passThroughParam = event.optin.ref 26 | 27 | console.log("Received authentication for user %d and page %d with pass " + 28 | "through param '%s' at %d", senderID, recipientID, passThroughParam, 29 | timeOfAuth) 30 | 31 | const details = await fbapi.getFBUserDetails(senderID) 32 | 33 | // When an authentication is received, we'll send a message back to the sender 34 | // to let them know it was successful. 35 | const getStarted = "To get started, try saying with either 'trending artists' or 'new articles' while we are on staging trending artists can be a bit naff." 36 | const welcome = `Welcome ${details.first_name} to the Artsy bot.\n${getStarted} \n\nWould you like to sign up for new daily art world articles?` 37 | 38 | fbapi.quickReply(senderID, welcome, [ 39 | { content_type: "text", title: "Yes please", payload: SettingsArticleSubscriptionUpdateKey }, 40 | { content_type: "text", title: "No thanks", payload: MainMenuKey } 41 | ]) 42 | } 43 | -------------------------------------------------------------------------------- /source/bot/webhook.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { $Request, $Response } from "express" 4 | import { api } from "../facebook/api" 5 | import { receivedAuthentication } from "./user-setup" 6 | import { exampleFallbacks } from "./facebook_examples" 7 | import { handlePostbacks } from "./postback-manager" 8 | import { getOrCreateMitosisUser } from "../db/mongo" 9 | import { handleUnknownMessage } from "./message-parser" 10 | import { receivedAccountLink } from "../facebook/facebook-user-login" 11 | 12 | export function botResponse(req: $Request, res: $Response) { 13 | var data: any = req.body 14 | 15 | // Make sure this is a page subscription 16 | if (data.object === "page") { 17 | // Iterate over each entry 18 | // There may be multiple if batched 19 | data.entry.forEach(function(pageEntry: any) { 20 | // var pageID = pageEntry.id 21 | // var timeOfEvent = pageEntry.time 22 | 23 | // Iterate over each messaging event 24 | pageEntry.messaging.forEach(function(messagingEvent: any) { 25 | if (messagingEvent.optin) { 26 | receivedAuthentication(messagingEvent) 27 | } else if (messagingEvent.message) { 28 | receivedMessage(messagingEvent) 29 | } else if (messagingEvent.delivery) { 30 | receivedDeliveryConfirmation(messagingEvent) 31 | } else if (messagingEvent.postback) { 32 | receivedPostback(messagingEvent) 33 | } else if (messagingEvent.read) { 34 | receivedMessageRead(messagingEvent) 35 | } else if (messagingEvent.account_linking) { 36 | receivedAccountLink(messagingEvent) 37 | } else { 38 | console.log("Webhook received unknown messagingEvent: ", messagingEvent) 39 | } 40 | }) 41 | }) 42 | 43 | // Assume all went well. 44 | // 45 | // You must send back a 200, within 20 seconds, to let us know you've 46 | // successfully received the callback. Otherwise, the request will time out. 47 | res.sendStatus(200) 48 | } 49 | } 50 | 51 | /* 52 | * Message Event 53 | * 54 | * This event is called when a message is sent to your page. The 'message' 55 | * object format can vary depending on the kind of message that was received. 56 | * Read more at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-received 57 | * 58 | */ 59 | async function receivedMessage(event: any) { 60 | var senderID = event.sender.id 61 | var recipientID = event.recipient.id 62 | 63 | // TODO: Get a user access token from a db 64 | const context = await getOrCreateMitosisUser(senderID) 65 | 66 | var timeOfMessage = event.timestamp 67 | var message = event.message 68 | 69 | console.log("Received message for user %d and page %d at %d with message:", 70 | senderID, recipientID, timeOfMessage) 71 | console.log(JSON.stringify(message)) 72 | 73 | var isEcho = message.is_echo 74 | var messageId = message.mid 75 | var appId = message.app_id 76 | var metadata = message.metadata 77 | 78 | // You may get a text or attachment but not both 79 | var messageText = message.text 80 | var messageAttachments = message.attachments 81 | var quickReply = message.quick_reply 82 | 83 | if (isEcho) { 84 | // Just logging message echoes to console 85 | console.log("Received echo for message %s and app %d with metadata %s", 86 | messageId, appId, metadata) 87 | return 88 | } else if (quickReply) { 89 | var quickReplyPayload = quickReply.payload 90 | console.log("Quick reply for message %s with payload %s", 91 | messageId, quickReplyPayload) 92 | 93 | handlePostbacks(context, quickReplyPayload) 94 | return 95 | } 96 | 97 | if (messageText) { 98 | if (handleUnknownMessage(context, messageText, metadata) === false) { 99 | // This is Facebook's example responses, keeping around demoing 100 | exampleFallbacks(senderID, messageText) 101 | } 102 | } else if (messageAttachments) { 103 | sendTextMessage(senderID, "Message with attachment received") 104 | } 105 | } 106 | 107 | /* 108 | * Delivery Confirmation Event 109 | * 110 | * This event is sent to confirm the delivery of a message. Read more about 111 | * these fields at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-delivered 112 | * 113 | */ 114 | function receivedDeliveryConfirmation(event: any) { 115 | // var senderID = event.sender.id 116 | // var recipientID = event.recipient.id 117 | var delivery = event.delivery 118 | var messageIDs = delivery.mids 119 | var watermark = delivery.watermark 120 | // var sequenceNumber = delivery.seq 121 | 122 | if (messageIDs) { 123 | messageIDs.forEach(function(messageID: string) { 124 | console.log("Received delivery confirmation for message ID: %s", 125 | messageID) 126 | }) 127 | } 128 | 129 | console.log("All message before %d were delivered.", watermark) 130 | } 131 | 132 | /* 133 | * Postback Event 134 | * 135 | * This event is called when a postback is tapped on a Structured Message. 136 | * https://developers.facebook.com/docs/messenger-platform/webhook-reference/postback-received 137 | * 138 | */ 139 | async function receivedPostback(event: any) { 140 | var senderID = event.sender.id 141 | var recipientID = event.recipient.id 142 | var timeOfPostback = event.timestamp 143 | 144 | // The 'payload' param is a developer-defined field which is set in a postback 145 | // button for Structured Messages. 146 | var payload = event.postback.payload 147 | 148 | console.log("Received postback for user %d and page %d with payload '%s' " + 149 | "at %d", senderID, recipientID, payload, timeOfPostback) 150 | 151 | const context = await getOrCreateMitosisUser(senderID) 152 | handlePostbacks(context, payload) 153 | } 154 | 155 | /* 156 | * Message Read Event 157 | * 158 | * This event is called when a previously-sent message has been read. 159 | * https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-read 160 | * 161 | */ 162 | function receivedMessageRead(event: any) { 163 | // var senderID = event.sender.id 164 | // var recipientID = event.recipient.id 165 | 166 | // All messages before watermark (a timestamp) or sequence have been seen. 167 | var watermark = event.read.watermark 168 | var sequenceNumber = event.read.seq 169 | 170 | console.log("Received message read event for watermark %d and sequence " + 171 | "number %d", watermark, sequenceNumber) 172 | } 173 | 174 | /* 175 | * Send a text message using the Send API. Deprecated in favour of the fbapi's version' 176 | * 177 | */ 178 | export function sendTextMessage(recipientId: string, messageText: string, metadata: string = "NO_CONTEXT"): Promise { 179 | var messageData = { 180 | recipient: { 181 | id: recipientId 182 | }, 183 | message: { 184 | text: messageText, 185 | metadata: metadata 186 | } 187 | } 188 | 189 | return api(messageData) 190 | } 191 | 192 | -------------------------------------------------------------------------------- /source/db/mongo.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const mongojs = require("mongojs") 4 | 5 | import { fbapi } from "../facebook/api" 6 | 7 | import type { MitosisUser } from "../bot/types" 8 | import { DB_URL } from "../globals" 9 | 10 | const db = mongojs(`mongodb://${DB_URL}`, ["users", "token_lookup"]) 11 | 12 | /** 13 | * Gets a User for a fb sender ID 14 | * 15 | * @param {string} senderID 16 | * @returns {Promise} the User account representation in the db 17 | */ 18 | 19 | export async function getOrCreateMitosisUser(senderID: string): Promise { 20 | return new Promise((resolve: any, reject: any) => { 21 | // Check for existence 22 | db.users.findOne({ fbSenderID: senderID }, async (err, doc) => { 23 | if (err) { return reject(err) } 24 | if (doc) { return resolve(doc) } 25 | 26 | // Make a new one if not 27 | let fbData = { timezone: 1, first_name: "The Bot" } 28 | try { 29 | // Ensure that we have all the data we want from fb 30 | // The actual fb page sometimes calls this, so it can fall through 31 | fbData = await fbapi.getFBUserDetails(senderID) 32 | } catch (e) {} 33 | 34 | // Insert a new model 35 | const newUser: MitosisUser = { 36 | fbSenderID: senderID, 37 | subscribeToArticlesBiDaily: false, 38 | timeForBiDailySub: 0, 39 | timezoneOffset: fbData.timezone, 40 | firstName: fbData.first_name 41 | } 42 | db.users.insert(newUser, (err, doc) => { 43 | if (err) { return reject(err) } 44 | if (doc) { return resolve(doc) } 45 | }) 46 | }) 47 | }) 48 | } 49 | 50 | /** 51 | * Update the db representation of the user 52 | * 53 | * @param {MitosisUser} user The user JSON to update in the db 54 | */ 55 | export function updateMitosisUser(user: MitosisUser): Promise { 56 | return new Promise((resolve: any, reject: any) => { 57 | db.users.update({ fbSenderID: user.fbSenderID }, {$set: user}, () => { 58 | resolve(user) 59 | }) 60 | }) 61 | } 62 | 63 | /** 64 | * Look for users with the right GMT settings for a notification 65 | * 66 | * @param {number} hour The hour you're looking for 67 | */ 68 | export function findAllUsersWithGMTHour(hour: number): Promise { 69 | return new Promise((resolve: any, reject: any) => { 70 | db.users.find({ subscribeToArticlesBiDaily: true, renderedGMTTimeForArticles: hour }, (err, docs) => { 71 | console.log(docs) 72 | if (err) { reject(err) } 73 | else { resolve(docs) } 74 | }) 75 | }) 76 | } 77 | 78 | /** 79 | * Assumes a lookup-like shape 80 | * 81 | * @export 82 | * @param {string} senderID 83 | * @returns {Promise} 84 | */ 85 | export async function getOrCreateArtsyLookup(object: any): Promise { 86 | return new Promise((resolve: any, reject: any) => { 87 | // Check for existence 88 | db.token_lookup.findOne({ requestID: object.requestID }, async (err, doc) => { 89 | if (err) { return reject(err) } 90 | if (doc) { return resolve(doc) } 91 | 92 | db.token_lookup.insert(object, (err, doc) => { 93 | if (err) { return reject(err) } 94 | if (doc) { return resolve(doc) } 95 | }) 96 | }) 97 | }) 98 | } 99 | 100 | /** 101 | * Update the db representation of the user 102 | * 103 | * @param {MitosisUser} user The user JSON to update in the db 104 | */ 105 | export function updateArtsyLookup(tokenLookup: any): Promise { 106 | return new Promise((resolve: any, reject: any) => { 107 | db.token_lookup.update({ requestID: tokenLookup.requestID }, {$set: tokenLookup}, () => { 108 | resolve(tokenLookup) 109 | }) 110 | }) 111 | } 112 | 113 | /** 114 | * Gets the lookup token via requestID, then deletes it afterwards 115 | * returning the one found 116 | * 117 | * @param {MitosisUser} user The user JSON to update in the db 118 | */ 119 | export function getAndDeleteArtsyLookup(tokenLookup: any): Promise { 120 | return new Promise((resolve: any, reject: any) => { 121 | db.token_lookup.findOne({ requestID: tokenLookup.requestID }, async (err, doc) => { 122 | if (err) { return reject(err) } 123 | if (doc) { 124 | db.token_lookup.remove({ requestID: tokenLookup.requestID }, () => { 125 | return resolve(doc) 126 | }) 127 | } 128 | }) 129 | }) 130 | } 131 | -------------------------------------------------------------------------------- /source/facebook/api.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import fetch from "node-fetch" 4 | import { PAGE_ACCESS_TOKEN, BOT_SERVER_URL } from "../globals" 5 | import type { QuickReply, GenericElement, FBButton } from "./types" 6 | 7 | export const fbapi = { 8 | /** 9 | * Present a collection of small tappable actions, with a message body beforehand. 10 | * Feel free to send nulls in the array, they will be filtered. Title cannot be empty. 11 | * 12 | * @export 13 | * @param {string} recipientId ID of person to send data to 14 | * @param {string} title Message body 15 | * @param {QuickReply[]} replies Array of replies 16 | * @returns {Promise} The JSON response if everything is all good 17 | */ 18 | quickReply(recipientId: string, title: ?string, replies: Array) { 19 | // Break the types so that we can go from `?QuickReply` to `QuickReply` 20 | const onlyReplies:any[] = replies.filter((r) => r !== null).slice(0, 10) 21 | return api({ 22 | recipient: { 23 | id: recipientId 24 | }, 25 | message: { 26 | text: (title && title.length > 0) ? title.slice(0, 319) : null, 27 | quick_replies: onlyReplies.map((r) => sanitiseQuickReply(r)) 28 | } 29 | }) 30 | }, 31 | 32 | /** 33 | * Send a notification that we're thinking 34 | * 35 | * @param {string} recipientId 36 | */ 37 | startTyping(recipientId: string) { 38 | return api({ 39 | recipient: { 40 | id: recipientId 41 | }, 42 | sender_action: "typing_on" 43 | }) 44 | }, 45 | 46 | /** 47 | * Turn off notifications that we're thinking 48 | * 49 | * @param {string} recipientId 50 | */ 51 | stopTyping(recipientId: string) { 52 | return api({ 53 | recipient: { 54 | id: recipientId 55 | }, 56 | sender_action: "typing_off" 57 | }) 58 | }, 59 | 60 | /** 61 | * Send a collection of elements, effectively making a carousel 62 | * 63 | * @param {string} recipientId 64 | * @param {GenericElement} elements 65 | */ 66 | async elementCarousel(recipientId: string, title: string, elements: GenericElement[], replies: Array) { 67 | if (title && title.length) { 68 | await this.sendTextMessage(recipientId, "> " + title) 69 | } 70 | 71 | const onlyReplies:any[] = replies.filter((r) => r !== null) 72 | const safeReplies = onlyReplies.map((r) => sanitiseQuickReply(r)).slice(0, 10) 73 | const repliesAPI = onlyReplies.length > 0 ? { quick_replies: safeReplies } : {} 74 | 75 | return api({ 76 | recipient: { 77 | id: recipientId 78 | }, 79 | message: { 80 | ...repliesAPI, 81 | attachment: { 82 | type: "template", 83 | payload: { 84 | template_type: "generic", 85 | elements: elements.slice(0, 10).map((e) => sanitiseElement(e)) 86 | } 87 | } 88 | } 89 | }) 90 | }, 91 | 92 | /** 93 | * Sends a message chopped up into 320 characters 94 | * 95 | * @param {string} recipientId 96 | * @param {string} messageText 97 | * @param {string} [metadata="NO_CONTEXT"] 98 | */ 99 | async sendLongMessage(recipientId: string, messageText: string, metadata: string = "NO_CONTEXT") { 100 | const messages = messageText.match(/.{1,319}/g) 101 | if (messages) { 102 | for (const message of messages) { 103 | await this.sendTextMessage(recipientId, message, metadata) 104 | } 105 | } 106 | }, 107 | /** 108 | * Sends a text message 109 | * 110 | * @param {string} recipientId 111 | * @param {string} messageText 112 | * @param {string} [metadata="NO_CONTEXT"] 113 | */ 114 | sendTextMessage(recipientId: string, messageText: string, metadata: string = "NO_CONTEXT") { 115 | if (messageText === undefined || messageText.length === 0) { 116 | console.error("Empty string sent to messageText") 117 | return {} 118 | } 119 | return api({ 120 | recipient: { 121 | id: recipientId 122 | }, 123 | message: { 124 | text: messageText.slice(0, 319), 125 | metadata: metadata 126 | } 127 | }) 128 | }, 129 | 130 | /** 131 | * Gets the user details for a Facebook, note this is intentionally restricted to details we can see 132 | * https://developers.facebook.com/docs/messenger-platform/user-profile 133 | * 134 | * @param {string} recipientId user 135 | */ 136 | async getFBUserDetails(recipientId: string) { 137 | const fields = "first_name,timezone" 138 | const url = `https://graph.facebook.com/v2.6/${recipientId}?fields=${fields}&access_token=${PAGE_ACCESS_TOKEN}` 139 | return fetch(url, {method: "GET"}).then(checkStatus).then(parseJSON) 140 | }, 141 | 142 | /** Starts a browser based oauth flow */ 143 | showLoginScreen(recipientId: string) { 144 | return api({ 145 | recipient: { 146 | id: recipientId 147 | }, 148 | message: { 149 | attachment: { 150 | type: "template", 151 | payload: { 152 | template_type: "button", 153 | text: "Welcome. To link your account, please click below.", 154 | buttons: [{ 155 | type: "account_link", 156 | url: `${BOT_SERVER_URL}/authorize` 157 | }] 158 | } 159 | } 160 | } 161 | }) 162 | }, 163 | /** Removes the browser based oauth flow */ 164 | showLogout(recipientId: string) { 165 | return api({ 166 | recipient: { 167 | id: recipientId 168 | }, 169 | message: { 170 | attachment: { 171 | type: "template", 172 | payload: { 173 | template_type: "button", 174 | text: "No problem, click below to log out.", 175 | buttons: [{ 176 | type: "account_unlink" 177 | }] 178 | } 179 | } 180 | } 181 | }) 182 | } 183 | } 184 | 185 | // NOTE: 186 | // The FB API doesn't crop texts being sent to it, so we'll do it before sending anything up 187 | // meaning the app itself doesn't care about implementation details like that. 188 | 189 | function sanitiseQuickReply(reply: QuickReply): QuickReply { 190 | var safeReply: QuickReply = { 191 | title: reply.title.slice(0, 19), 192 | content_type: reply.content_type, 193 | payload: reply.payload || "" 194 | } 195 | return safeReply 196 | } 197 | 198 | /** 199 | * Converts a button into one that won't fail the network request 200 | * 201 | * @param {FBButton} button 202 | * @returns {FBButton} safe version of the button 203 | */ 204 | function sanitiseButton(button: FBButton): FBButton { 205 | var safeButton: FBButton = { 206 | type: button.type, 207 | title: button.title.slice(0, 30) 208 | } 209 | if (button.url) { safeButton.url = button.url } 210 | if (button.payload) { safeButton.payload = button.payload.slice(0, 319) } 211 | return safeButton 212 | } 213 | 214 | /** 215 | * Converts a GenericElement into a safe version for the API 216 | * 217 | * @param {GenericElement} element 218 | * @returns {GenericElement} a afer version 219 | */ 220 | function sanitiseElement(element: GenericElement): GenericElement { 221 | var safeElement: GenericElement = { 222 | title: element.title.slice(0, 60), 223 | item_url: element.item_url, 224 | image_url: element.image_url 225 | } 226 | if (element.subtitle) { safeElement.subtitle = element.subtitle.slice(0, 319) } 227 | if (element.buttons) { safeElement.buttons = element.buttons.slice(0, 6).map((e) => sanitiseButton(e)) } 228 | return safeElement 229 | } 230 | 231 | // TODO, migrate this function away from being exposed 232 | 233 | /** 234 | * Makes a call to the Facebook API, and returns a promise wrapping the JSON body for 235 | * the response. Handles some rudimentary error loggin 236 | * 237 | * @param {any} message the json to send to FB 238 | * @returns {Promise} The JSON response if everything is all good 239 | */ 240 | export function api(message: any): Promise { 241 | console.log("Sending API thing:") 242 | console.log(JSON.stringify(message)) 243 | 244 | return fetch(`https://graph.facebook.com/v2.6/me/messages?access_token=${PAGE_ACCESS_TOKEN}`, { 245 | method: "POST", 246 | body: JSON.stringify(message), 247 | headers: { "Content-Type": "application/json" } 248 | }) 249 | .then(checkStatus) 250 | .then(parseJSON) 251 | .then((body: any) => { 252 | var recipientId = body.recipient_id 253 | var messageId = body.message_id 254 | 255 | if (messageId) { 256 | console.log("Successfully sent message with id %s to recipient %s", 257 | messageId, recipientId) 258 | } else { 259 | console.log("Successfully called Send API for recipient %s", 260 | recipientId) 261 | } 262 | return body 263 | }) 264 | } 265 | 266 | function checkStatus(response: any) { 267 | if (response.status >= 200 && response.status < 300) { 268 | return response 269 | } else { 270 | response.json().then(json => { 271 | console.error("\n\nResponse from FB error") 272 | console.error(JSON.stringify(json)) 273 | }) 274 | var error = new NetworkError(response.statusText) 275 | error.response = response 276 | throw error 277 | } 278 | } 279 | 280 | function parseJSON(response: any) { 281 | return response.json() 282 | } 283 | 284 | /** 285 | * An Error type with an attached network response 286 | * 287 | * @extends {Error} 288 | */ 289 | export class NetworkError extends Error { 290 | response: any 291 | } 292 | -------------------------------------------------------------------------------- /source/facebook/facebook-comms.js: -------------------------------------------------------------------------------- 1 | import crypto from "crypto" 2 | import { APP_SECRET } from "../mitosis" 3 | import type { $Request, $Response } from "express" 4 | 5 | // NOT USED ATM 6 | 7 | /* 8 | * Verify that the callback came from Facebook. Using the App Secret from 9 | * the App Dashboard, we can verify the signature that is sent with each 10 | * callback in the x-hub-signature field, located in the header. 11 | * 12 | * https://developers.facebook.com/docs/graph-api/webhooks#setup 13 | * 14 | */ 15 | export function verifyRequestSignature(req: $Request, res: $Response, buf: any) { 16 | var signature = req.headers["x-hub-signature"] 17 | 18 | if (!signature) { 19 | // For testing, let's log an error. In production, you should throw an 20 | // error. 21 | console.error("Couldn't validate the signature.") 22 | } else { 23 | var elements = signature.split("=") 24 | var signatureHash = elements[1] 25 | 26 | var expectedHash = crypto.createHmac("sha1", APP_SECRET) 27 | .update(buf) 28 | .digest("hex") 29 | 30 | if (signatureHash !== expectedHash) { 31 | throw new Error("Couldn't validate the request signature.") 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/facebook/facebook-user-login.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { $Request, $Response } from "express" 4 | import { VALIDATION_TOKEN, BOT_SERVER_URL } from "../globals" 5 | import { randomBytes } from "crypto" 6 | import { fbapi } from "../facebook/api" 7 | import { 8 | getOrCreateArtsyLookup, 9 | updateArtsyLookup, 10 | getAndDeleteArtsyLookup, 11 | getOrCreateMitosisUser, 12 | updateMitosisUser 13 | } from "../db/mongo" 14 | import { oauthLoginURL, gravity, GravityMeAPI } from "../bot/artsy-api" 15 | import { Cities } from "places" 16 | 17 | export function validation(req: $Request, res: $Response) { 18 | if (req.query["hub.mode"] === "subscribe" && 19 | req.query["hub.verify_token"] === VALIDATION_TOKEN) { 20 | console.log("Validating webhook") 21 | res.status(200).send(req.query["hub.challenge"]) 22 | } else { 23 | console.error("Failed validation. Make sure the validation tokens match.") 24 | res.sendStatus(403) 25 | } 26 | } 27 | 28 | // Sorry reader, authentication is a tricky tricky beast. 29 | // Facebook don't want to give us any user creds until everything 30 | // is confirmed, so we have to jump through hoops. 31 | // 32 | // Here's what happens: 33 | // 34 | // A user requests a "login" button, this goes to facebook, who then 35 | // redirect the user to `authoriseFacebook` in a web-browser. 36 | // 37 | // FB gives a param for `redirect_uri` which is the URL we need to send 38 | // to FB to confirm everything happened at the end. 39 | // 40 | // We auto-redirect users from `authoriseFacebook` to an Artsy Oauth URL, 41 | // this cannot be the messenger.com `redirect_uri` because we need to keep track 42 | // of the request ID. So, the Artsy Oauth flow redirects to `authoriseArtsy` 43 | // in the browser. 44 | // 45 | // `authoriseArtsy` then takes the Artsy Oauth token, and the request ID, stores 46 | // them together in the lookup db, and redirects to the original `redirect_uri`. 47 | // 48 | // This tells Facebook we're done authenticating, once that is all confirmed 49 | // we are sent a webhook notification that the accounts are linked, this is 50 | // what `receivedAccountLink` recieves. 51 | // 52 | // It takes the app link request ID and looks up the auth in the lookup db, 53 | // if it is successful, then it deletes it and sets the OAuth token on the mitosis 54 | // user account and we are done. Phew. 55 | 56 | // We're using this auth flow: 57 | // https://github.com/artsy/gravity/blob/master/doc/ApiAuthentication.md#login-using-oauth-web-browser-redirects 58 | 59 | // An express route for initially setting up authorization 60 | export async function authoriseFacebook(req: $Request, res: $Response) { 61 | // Facebook's user token for the authentication setup 62 | // we do not get access to the messenger UUID until everything 63 | // is confirmed, so we need to keep track 64 | 65 | var accountLinkingToken = req.query.account_linking_token 66 | var redirectURI = req.query.redirect_uri 67 | var requestID = randomBytes(20).toString("hex") 68 | 69 | // Create this for now, we will need it later when we get an 70 | // Auth token back from Artsy 71 | const newLookup = { 72 | requestID, 73 | accountLinkingToken, 74 | redirectURI 75 | } 76 | await getOrCreateArtsyLookup(newLookup) 77 | 78 | // Redirect users to this URI on successful login 79 | const redirect = `${BOT_SERVER_URL}authorize-artsy?requestID=${requestID}` 80 | const artsyURL = oauthLoginURL(redirect) 81 | 82 | res.redirect(artsyURL) 83 | } 84 | 85 | // An express route for recieving the Arty Oauth results, storing them 86 | // then passing a user forwards to messenger.com 87 | export async function authoriseArtsy(req: $Request, res: $Response) { 88 | // Facebook's user token for the authentication setup 89 | // we do not get access to the messenger UUID until everything 90 | // is confirmed, so we need to keep track 91 | 92 | const requestID = req.query.requestID 93 | 94 | // This returns a code, which we can turn into a token whenever we want 95 | const oauthCode = req.query.code 96 | // Store it so we can generate access tokens on the next gravity request 97 | const creds = await getOrCreateArtsyLookup({ requestID }) 98 | creds.artsyOauthAppCode = oauthCode 99 | await updateArtsyLookup(creds) 100 | 101 | // Redirect users to this URI on successful login 102 | var redirectURISuccess = creds.redirectURI + "&authorization_code=" + requestID 103 | res.redirect(redirectURISuccess) 104 | } 105 | 106 | /* 107 | * Account Link Event from the webhook 108 | * 109 | * This event is called when the Link Account or UnLink Account action has been 110 | * tapped. 111 | * https://developers.facebook.com/docs/messenger-platform/webhook-reference/account-linking 112 | * 113 | */ 114 | export async function receivedAccountLink(event: any) { 115 | var senderID = event.sender.id 116 | 117 | var status = event.account_linking.status 118 | var authCode = event.account_linking.authorization_code 119 | 120 | fbapi.startTyping(senderID) 121 | 122 | const lookup = await getAndDeleteArtsyLookup({ requestID: authCode }) 123 | const mitosisUser = await getOrCreateMitosisUser(senderID) 124 | 125 | mitosisUser.artsyOauthAppCode = lookup.artsyOauthAppCode 126 | 127 | const artsyMe = await gravity(GravityMeAPI, mitosisUser) 128 | // Use the /me API to figure out if they are in a big city, store that 129 | // for doing local shows 130 | if (artsyMe.location.city.length !== 0) { 131 | const citySlug: string = artsyMe.location.city.toLowerCase().replace(" ", "-") 132 | const maybeCity: ?any = Cities.find((c) => c.slug === citySlug) 133 | if (maybeCity) { 134 | mitosisUser.artsyLocationCitySlug = maybeCity.slug 135 | } 136 | } 137 | 138 | await updateMitosisUser(mitosisUser) 139 | 140 | fbapi.sendTextMessage(senderID, "OK - connected to Artsy!") 141 | console.log("Received account link event with for user %d with status %s " + 142 | "and auth code %s ", senderID, status, authCode) 143 | } 144 | -------------------------------------------------------------------------------- /source/facebook/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /** A structre used for triggering quick replies */ 4 | export interface QuickReply { 5 | content_type: "text", 6 | title: string, 7 | payload?: string 8 | } 9 | 10 | /** Button for attaching to images */ 11 | export interface FBButton { 12 | type: "web_url" | "postback", 13 | title: string, 14 | url?: ?string, 15 | payload?: ?string 16 | } 17 | 18 | export interface GenericElement { 19 | title: string, 20 | subtitle?: string, 21 | item_url: string, 22 | image_url: string, 23 | buttons?: Array 24 | } 25 | -------------------------------------------------------------------------------- /source/globals.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import config from "config" 4 | 5 | /** 6 | * Pulls out an env var from either the host ENV, or a config file 7 | * 8 | * @param {string} local ENV key 9 | * @param {string} configName Config key 10 | * @returns {string} 11 | */ 12 | function getEnv(local: string, configName: string): string { 13 | return (process.env[local]) ? process.env[local] : config.get(configName) 14 | } 15 | 16 | /** App Secret can be retrieved from the App Dashboard */ 17 | export const APP_SECRET = getEnv("MESSENGER_APP_SECRET", "appSecret") 18 | 19 | /** Arbitrary value used to validate a webhook */ 20 | export const VALIDATION_TOKEN = getEnv("MESSENGER_VALIDATION_TOKEN", "validationToken") 21 | 22 | /** Generate a page access token for your page from the App Dashboard */ 23 | export const PAGE_ACCESS_TOKEN = getEnv("MESSENGER_PAGE_ACCESS_TOKEN", "pageAccessToken") 24 | 25 | /** Artsy Client key */ 26 | export const ARTSY_API_CLIENT = getEnv("ARTSY_API_CLIENT", "artsyAPIClient") 27 | 28 | /** Artsy Client secret */ 29 | export const ARTSY_API_SECRET = getEnv("ARTSY_API_SECRET", "artsyAPISecret") 30 | 31 | /** This server address */ 32 | export const BOT_SERVER_URL = getEnv("BOT_SERVER_URL", "serverURL") 33 | 34 | /** The address for Gravity */ 35 | export const GRAVITY_URL = getEnv("GRAVITY_URL", "gravityURL") 36 | 37 | /** The address for Metaphysics */ 38 | export const METAPHYSICS_URL = getEnv("METAPHYSICS_URL", "metaphysicsURL") 39 | 40 | /** The front-end URL route */ 41 | export const WEB_URL = getEnv("WEB_URL", "webURL") 42 | 43 | /** Mongo db URL */ 44 | export const DB_URL = getEnv("DB_URL", "databaseURL") 45 | 46 | // Normal validation stuff 47 | if (!(APP_SECRET && VALIDATION_TOKEN && PAGE_ACCESS_TOKEN && BOT_SERVER_URL && ARTSY_API_CLIENT && ARTSY_API_SECRET && GRAVITY_URL && METAPHYSICS_URL && WEB_URL && DB_URL)) { 48 | console.error("Missing config values") 49 | process.exit(1) 50 | } 51 | -------------------------------------------------------------------------------- /source/mitosis.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import "babel-polyfill" 3 | 4 | import express from "express" 5 | import bodyParser from "body-parser" 6 | 7 | // import request from "request" 8 | // import fetch from "node-fetch" 9 | 10 | // import { verifyRequestSignature } from "./facebook/facebook-comms" 11 | // app.use(bodyParser.json({ verify: verifyRequestSignature })) 12 | 13 | require("./globals") 14 | require("./db/mongo") 15 | 16 | // Error logging 17 | process.on("unhandledRejection", function(reason: string, p: any) { 18 | console.log("Error: ", reason) 19 | }) 20 | 21 | const app = express() 22 | app.set("port", process.env.PORT || 5000) 23 | app.set("view engine", "ejs") 24 | app.use(bodyParser.json()) 25 | app.use(express.static("public")) 26 | 27 | // Authorization is a two-step process 28 | import { validation, authoriseFacebook, authoriseArtsy } from "./facebook/facebook-user-login" 29 | // Handles authorizing to FB 30 | app.get("/authorize", authoriseFacebook) 31 | // Handles authorizing to Artsy 32 | app.get("/authorize-artsy", authoriseArtsy) 33 | // Handles FB app setup requests 34 | app.get("/webhook", validation) 35 | 36 | import { botResponse } from "./bot/webhook" 37 | app.post("/webhook", botResponse) 38 | 39 | // Start server 40 | app.listen(app.get("port"), function() { 41 | console.log(`Started server at http://localhost:${process.env.PORT || 5000}`) 42 | }) 43 | -------------------------------------------------------------------------------- /source/scripts/hourly.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | require("../globals") 4 | require("../db/mongo") 5 | 6 | import { elementForArticle } from "../bot/contexts/article/element" 7 | import { newArticlesQuery } from "../bot/contexts/article/queries" 8 | import { showMainMenu } from "../bot/contexts/main-menu" 9 | 10 | import { metaphysicsQuery } from "../bot/artsy-api" 11 | import { fbapi } from "../facebook/api" 12 | import { findAllUsersWithGMTHour } from "../db/mongo" 13 | 14 | // Get the current hours offset from GMT 15 | // Derive the 3 potential slots for notifications 16 | // Get all users that are subscribed 17 | // Send them a hello message 18 | 19 | const roundToNearest = (round: number, numToRoundTo: number) => Math.round(round / numToRoundTo) * numToRoundTo 20 | 21 | const date = new Date() 22 | const currentHour = date.getHours() 23 | const gmtOffsetMinutes = date.getTimezoneOffset() 24 | 25 | // Not optimal, but it's a start, this lets us do work every hour 26 | const gmtOffsetHours = roundToNearest(gmtOffsetMinutes, 60) 27 | const hourWeCareAbout = currentHour - gmtOffsetHours 28 | 29 | console.log(`Handling anyone at GMT ${hourWeCareAbout}`) 30 | 31 | const sendUpdates = async (hour: number) => { 32 | const users = await findAllUsersWithGMTHour(hourWeCareAbout) 33 | 34 | console.log(`Sending notifications to ${users.length} people`) 35 | if (users.length === 0) { process.exit() } 36 | 37 | const firstUser = users[0] 38 | const results = await metaphysicsQuery(newArticlesQuery(), firstUser) 39 | 40 | const millisecondsPerDay = 1000 * 60 * 60 * 24 41 | const cutOffFilter = millisecondsPerDay * 1 42 | 43 | const yesterdaysArticles = results.data.articles.filter((a) => { 44 | return (Date.now() - Date.parse(a.published_at)) > cutOffFilter 45 | }) 46 | 47 | for (const user of users) { 48 | const message = `Hey there ${user.firstName}` 49 | await fbapi.elementCarousel(user.fbSenderID, message, yesterdaysArticles.map(a => elementForArticle(a)), []) 50 | await showMainMenu(user, "You can also explore Artsy") 51 | } 52 | 53 | process.exit() 54 | } 55 | 56 | sendUpdates(hourWeCareAbout) 57 | --------------------------------------------------------------------------------