├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitconfig ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .languagebabel ├── .tern-project ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bin ├── commands │ ├── acronym.js │ ├── completion.js │ ├── configuration.js │ ├── configuration │ │ ├── get.js │ │ ├── init.js │ │ └── set.js │ ├── datamuse.js │ ├── datamuse │ │ ├── get.js │ │ └── info.js │ ├── list.js │ ├── onelook.js │ ├── random.js │ ├── rhymebrain.js │ ├── rhymebrain │ │ ├── combine.js │ │ ├── info.js │ │ └── rhyme.js │ ├── urban.js │ ├── wordmap.js │ ├── wordnik.js │ └── wordnik │ │ ├── define.js │ │ ├── example.js │ │ ├── hyphen.js │ │ ├── origin.js │ │ ├── phrase.js │ │ ├── pronounce.js │ │ └── relate.js ├── leximaven.js ├── themes.js └── tools.js ├── default.config.noon ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── src ├── commands │ ├── acronym.js │ ├── completion.js │ ├── configuration.js │ ├── configuration │ │ ├── get.js │ │ ├── init.js │ │ └── set.js │ ├── datamuse.js │ ├── datamuse │ │ ├── get.js │ │ └── info.js │ ├── list.js │ ├── onelook.js │ ├── random.js │ ├── rhymebrain.js │ ├── rhymebrain │ │ ├── combine.js │ │ ├── info.js │ │ └── rhyme.js │ ├── urban.js │ ├── wordmap.js │ ├── wordnik.js │ └── wordnik │ │ ├── define.js │ │ ├── example.js │ │ ├── hyphen.js │ │ ├── origin.js │ │ ├── phrase.js │ │ ├── pronounce.js │ │ └── relate.js ├── leximaven.js ├── themes.js └── tools.js ├── test.sh └── themes ├── colonel.noon ├── markup.noon └── square.noon /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "compact": true, 4 | "env": { 5 | "dev": { 6 | "plugins": ["lodash"] 7 | }, 8 | "test": { 9 | "plugins": ["lodash"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [*.noon] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | ecmaFeatures: 2 | modules: true 3 | jsx: true 4 | 5 | env: 6 | amd: true 7 | browser: true 8 | es6: true 9 | jquery: true 10 | node: true 11 | 12 | # http://eslint.org/docs/rules/ 13 | rules: 14 | # Possible Errors 15 | comma-dangle: [2, never] 16 | no-cond-assign: 2 17 | no-console: 0 18 | no-constant-condition: 2 19 | no-control-regex: 2 20 | no-debugger: 2 21 | no-dupe-args: 2 22 | no-dupe-keys: 2 23 | no-duplicate-case: 2 24 | no-empty: 2 25 | no-empty-character-class: 2 26 | no-ex-assign: 2 27 | no-extra-boolean-cast: 2 28 | no-extra-parens: 0 29 | no-extra-semi: 2 30 | no-func-assign: 2 31 | no-inner-declarations: [2, functions] 32 | no-invalid-regexp: 2 33 | no-irregular-whitespace: 2 34 | no-negated-in-lhs: 2 35 | no-obj-calls: 2 36 | no-regex-spaces: 2 37 | no-sparse-arrays: 2 38 | no-unexpected-multiline: 2 39 | no-unreachable: 2 40 | use-isnan: 2 41 | valid-jsdoc: 0 42 | valid-typeof: 2 43 | 44 | # Best Practices 45 | accessor-pairs: 2 46 | block-scoped-var: 0 47 | complexity: [2, 6] 48 | consistent-return: 0 49 | curly: 0 50 | default-case: 0 51 | dot-location: 0 52 | dot-notation: 0 53 | eqeqeq: 2 54 | guard-for-in: 2 55 | no-alert: 2 56 | no-caller: 2 57 | no-case-declarations: 2 58 | no-div-regex: 2 59 | no-else-return: 0 60 | no-empty-label: 2 61 | no-empty-pattern: 2 62 | no-eq-null: 2 63 | no-eval: 2 64 | no-extend-native: 2 65 | no-extra-bind: 2 66 | no-fallthrough: 2 67 | no-floating-decimal: 0 68 | no-implicit-coercion: 0 69 | no-implied-eval: 2 70 | no-invalid-this: 0 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 2 74 | no-loop-func: 2 75 | no-magic-number: 0 76 | no-multi-spaces: 0 77 | no-multi-str: 0 78 | no-native-reassign: 2 79 | no-new-func: 2 80 | no-new-wrappers: 2 81 | no-new: 2 82 | no-octal-escape: 2 83 | no-octal: 2 84 | no-proto: 2 85 | no-redeclare: 2 86 | no-return-assign: 2 87 | no-script-url: 2 88 | no-self-compare: 2 89 | no-sequences: 0 90 | no-throw-literal: 0 91 | no-unused-expressions: 2 92 | no-useless-call: 2 93 | no-useless-concat: 2 94 | no-void: 2 95 | no-warning-comments: 0 96 | no-with: 2 97 | radix: 2 98 | vars-on-top: 0 99 | wrap-iife: 2 100 | yoda: 0 101 | 102 | # Strict 103 | strict: 0 104 | 105 | # Variables 106 | init-declarations: 0 107 | no-catch-shadow: 2 108 | no-delete-var: 2 109 | no-label-var: 2 110 | no-shadow-restricted-names: 2 111 | no-shadow: 0 112 | no-undef-init: 2 113 | no-undef: 0 114 | no-undefined: 0 115 | no-unused-vars: 0 116 | no-use-before-define: 0 117 | 118 | # Node.js and CommonJS 119 | callback-return: 2 120 | global-require: 2 121 | handle-callback-err: 2 122 | no-mixed-requires: 0 123 | no-new-require: 0 124 | no-path-concat: 2 125 | no-process-exit: 2 126 | no-restricted-modules: 0 127 | no-sync: 0 128 | 129 | # Stylistic Issues 130 | array-bracket-spacing: 0 131 | block-spacing: 0 132 | brace-style: 0 133 | camelcase: 0 134 | comma-spacing: 0 135 | comma-style: 0 136 | computed-property-spacing: 0 137 | consistent-this: 0 138 | eol-last: 0 139 | func-names: 0 140 | func-style: 0 141 | id-length: 0 142 | id-match: 0 143 | indent: 0 144 | jsx-quotes: 0 145 | key-spacing: 0 146 | linebreak-style: 0 147 | lines-around-comment: 0 148 | max-depth: 0 149 | max-len: 0 150 | max-nested-callbacks: 0 151 | max-params: 0 152 | max-statements: [2, 30] 153 | new-cap: 0 154 | new-parens: 0 155 | newline-after-var: 0 156 | no-array-constructor: 0 157 | no-bitwise: 0 158 | no-continue: 0 159 | no-inline-comments: 0 160 | no-lonely-if: 0 161 | no-mixed-spaces-and-tabs: 0 162 | no-multiple-empty-lines: 0 163 | no-negated-condition: 0 164 | no-nested-ternary: 0 165 | no-new-object: 0 166 | no-plusplus: 0 167 | no-restricted-syntax: 0 168 | no-spaced-func: 0 169 | no-ternary: 0 170 | no-trailing-spaces: 0 171 | no-underscore-dangle: 0 172 | no-unneeded-ternary: 0 173 | object-curly-spacing: 0 174 | one-var: 0 175 | operator-assignment: 0 176 | operator-linebreak: 0 177 | padded-blocks: 0 178 | quote-props: 0 179 | quotes: 0 180 | require-jsdoc: 0 181 | semi-spacing: 0 182 | semi: 0 183 | sort-vars: 0 184 | space-after-keywords: 0 185 | space-before-blocks: 0 186 | space-before-function-paren: 0 187 | space-before-keywords: 0 188 | space-in-parens: 0 189 | space-infix-ops: 0 190 | space-return-throw-case: 0 191 | space-unary-ops: 0 192 | spaced-comment: 0 193 | wrap-regex: 0 194 | 195 | # ECMAScript 6 196 | arrow-body-style: 0 197 | arrow-parens: 0 198 | arrow-spacing: 0 199 | constructor-super: 0 200 | generator-star-spacing: 0 201 | no-arrow-condition: 0 202 | no-class-assign: 0 203 | no-const-assign: 0 204 | no-dupe-class-members: 0 205 | no-this-before-super: 0 206 | no-var: 0 207 | object-shorthand: 0 208 | prefer-arrow-callback: 0 209 | prefer-const: 0 210 | prefer-reflect: 0 211 | prefer-spread: 0 212 | prefer-template: 0 213 | require-yield: 0 214 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | # drawnepicenter 2 | Host drawnepicenter.github.com 3 | HostName github.com 4 | PreferredAuthentications publickey 5 | IdentityFile ~/.ssh/de_rsa 6 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | node_modules 23 | .gitconfig 24 | 25 | # Project specific 26 | .sync 27 | docs 28 | scripts 29 | staging 30 | wiki 31 | TODO.md 32 | jsdoc.conf.json 33 | json 34 | .nvmrc 35 | .syncthing* 36 | lcov.info 37 | .babelrc 38 | .eslintignore 39 | .eslintrc 40 | .languagebabel 41 | .tern-project 42 | *.code-workspace 43 | yarn.lock 44 | ptags.fish 45 | -------------------------------------------------------------------------------- /.languagebabel: -------------------------------------------------------------------------------- 1 | { 2 | "babelMapsPath": "bin", 3 | "babelMapsAddUrl": true, 4 | "babelSourcePath": "src", 5 | "babelTranspilePath": "bin", 6 | "createMap": false, 7 | "createTargetDirectories": true, 8 | "createTranspiledCode": true, 9 | "disableWhenNoBabelrcFileInPath": true, 10 | "suppressSourcePathMessages": true, 11 | "suppressTranspileOnSaveMessages": true, 12 | "transpileOnSave": false 13 | } 14 | -------------------------------------------------------------------------------- /.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaVersion": 6, 3 | "libs": ["chai"], 4 | "loadEagerly": [ 5 | "src/**/*.js", 6 | "test/*.es6" 7 | ], 8 | "dontLoad": ["node_modules/**/*"], 9 | "plugins": { 10 | "doc_comment": { 11 | "fullDocs": true 12 | }, 13 | "complete_strings": { 14 | "maxLength": 25 15 | }, 16 | "modules": {}, 17 | "es_modules": {}, 18 | "eslint": { 19 | "config": { 20 | "env": { 21 | "browser": false 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '14' 4 | before_script: 5 | - npm i -g 6 | - leximaven config init 7 | script: ./test.sh -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | # [4.0.0](https://github.com/drawnepicenter/leximaven/compare/v3.0.0...v4.0.0) (2019-01-04) 7 | 8 | 9 | ### Features 10 | 11 | * **project:** resurrect ([793e1e4](https://github.com/drawnepicenter/leximaven/commit/793e1e4)), closes [#25](https://github.com/drawnepicenter/leximaven/issues/25) [#26](https://github.com/drawnepicenter/leximaven/issues/26) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * **project:** Right now anagrams are disabled. I'm effectively removing this feature for now 17 | 18 | 19 | 20 | 21 | # [3.0.0](https://github.com/drawnepicenter/leximaven/compare/v2.3.4...v3.0.0) (2017-08-30) 22 | 23 | 24 | ### Features 25 | 26 | * **project:** BREAKING_CHANGES:remove tests ([0113744](https://github.com/drawnepicenter/leximaven/commit/0113744)) 27 | 28 | 29 | ### BREAKING CHANGES 30 | 31 | * **project:** remove tests 32 | 33 | 34 | 35 | 36 | ## [2.3.4](https://github.com/drawnepicenter/leximaven/compare/v2.3.3...v2.3.4) (2017-02-05) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **wordmap:** require 'fs' ([e21d630](https://github.com/drawnepicenter/leximaven/commit/e21d630)) 42 | 43 | 44 | 45 | 46 | ## [2.3.3](https://github.com/drawnepicenter/leximaven/compare/v2.3.2...v2.3.3) (2017-01-29) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * **configuration:** missing command aliases ([d368b8d](https://github.com/drawnepicenter/leximaven/commit/d368b8d)) 52 | 53 | 54 | 55 | 56 | ## [2.3.2](https://github.com/drawnepicenter/leximaven/compare/v2.3.1...v2.3.2) (2017-01-29) 57 | 58 | 59 | ### Bug Fixes 60 | 61 | * **wordmap:** mapping doesn't work in other directories as a package ([17b01e1](https://github.com/drawnepicenter/leximaven/commit/17b01e1)) 62 | 63 | 64 | 65 | 66 | ## [2.3.1](https://github.com/drawnepicenter/leximaven/compare/v2.3.0...v2.3.1) (2017-01-25) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * **project:** config init error ([8b0bfa1](https://github.com/drawnepicenter/leximaven/commit/8b0bfa1)) 72 | * **themes:** control fallback msg with verbose ([9e3de72](https://github.com/drawnepicenter/leximaven/commit/9e3de72)), closes [#16](https://github.com/drawnepicenter/leximaven/issues/16) 73 | 74 | 75 | 76 | 77 | # [2.3.0](https://github.com/drawnepicenter/leximaven/compare/v2.2.0...v2.3.0) (2017-01-23) 78 | 79 | 80 | ### Features 81 | 82 | * **project:** add random word command ([4d782be](https://github.com/drawnepicenter/leximaven/commit/4d782be)), closes [#14](https://github.com/drawnepicenter/leximaven/issues/14) 83 | 84 | 85 | 86 | 87 | # [2.2.0](https://github.com/drawnepicenter/leximaven/compare/v2.1.0...v2.2.0) (2017-01-21) 88 | 89 | 90 | ### Features 91 | 92 | * **project:** add command aliases ([4597a8c](https://github.com/drawnepicenter/leximaven/commit/4597a8c)) 93 | 94 | 95 | 96 | 97 | # [2.1.0](https://github.com/drawnepicenter/leximaven/compare/v2.0.0...v2.1.0) (2017-01-09) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * **tools:** move fs-extra to runtime deps ([95067ab](https://github.com/drawnepicenter/leximaven/commit/95067ab)) 103 | 104 | 105 | ### Features 106 | 107 | * **tools:** add functions from iloa ([35ceabd](https://github.com/drawnepicenter/leximaven/commit/35ceabd)) 108 | 109 | 110 | 111 | 112 | # [2.0.0](https://github.com/drawnepicenter/leximaven/compare/v1.2.0...v2.0.0) (2016-09-15) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * **project:** proper HTTP error msg ([9eef640](https://github.com/drawnepicenter/leximaven/commit/9eef640)) 118 | 119 | 120 | ### Features 121 | 122 | * **project:** rename build dir to bin, simplify gulpfile, simplify command file and dir names ([208250e](https://github.com/drawnepicenter/leximaven/commit/208250e)) 123 | 124 | 125 | ### BREAKING CHANGES 126 | 127 | * project: See items listed in #12 128 | 129 | 130 | 131 | 132 | # [1.2.0](https://github.com/drawnepicenter/leximaven/compare/v1.1.0...v1.2.0) (2016-08-26) 133 | 134 | 135 | ### Bug Fixes 136 | 137 | * **wordnik:** remove url from outfile ([2dac4dc](https://github.com/drawnepicenter/leximaven/commit/2dac4dc)), closes [#8](https://github.com/drawnepicenter/leximaven/issues/8) 138 | 139 | 140 | ### Features 141 | 142 | * **wordnik:** check for API key ([bbf9dab](https://github.com/drawnepicenter/leximaven/commit/bbf9dab)) 143 | 144 | 145 | 146 | 147 | # [1.1.0](https://github.com/drawnepicenter/leximaven/compare/v1.0.2...v1.1.0) (2016-08-26) 148 | 149 | 150 | ### Bug Fixes 151 | 152 | * **rbrain:** Add timestamp expired msg to usage info ([422df3c](https://github.com/drawnepicenter/leximaven/commit/422df3c)) 153 | 154 | 155 | ### Features 156 | 157 | * **dmuse:** cached response won't decrement usage ([3973511](https://github.com/drawnepicenter/leximaven/commit/3973511)) 158 | * **onelook:** cached response won't decrement usage ([01bd9f0](https://github.com/drawnepicenter/leximaven/commit/01bd9f0)) 159 | * **rbrain:** cached response won't decrement usage ([613be73](https://github.com/drawnepicenter/leximaven/commit/613be73)) 160 | * **wordnik:** cached response won't decrement usage ([9783ea8](https://github.com/drawnepicenter/leximaven/commit/9783ea8)) 161 | 162 | 163 | 164 | 165 | ## [1.0.2](https://github.com/drawnepicenter/leximaven/compare/v1.0.1...v1.0.2) (2016-08-19) 166 | 167 | 168 | ### Bug Fixes 169 | 170 | * **CI:** remove strip-ansi require ([48efa39](https://github.com/drawnepicenter/leximaven/commit/48efa39)) 171 | * **cli-tests:** stringify error ([9c9932d](https://github.com/drawnepicenter/leximaven/commit/9c9932d)) 172 | * **config-init:** no more JSON stringify creating problems with babel-polyfill ([8a9dca8](https://github.com/drawnepicenter/leximaven/commit/8a9dca8)), closes [#6](https://github.com/drawnepicenter/leximaven/issues/6) 173 | 174 | 175 | 176 | 177 | ## [1.0.1](https://github.com/drawnepicenter/leximaven/compare/v1.0.0...v1.0.1) (2016-08-17) 178 | 179 | 180 | 181 | 182 | # [1.0.0](https://github.com/drawnepicenter/leximaven/compare/v0.1.4...v1.0.0) (2016-08-17) 183 | 184 | 185 | ### Bug Fixes 186 | 187 | * **CI:** don't test on 4.x or iojs ([7380d8c](https://github.com/drawnepicenter/leximaven/commit/7380d8c)) 188 | * **CI:** theme dir gets set correctly ([194e123](https://github.com/drawnepicenter/leximaven/commit/194e123)) 189 | * **onelook:** extract strings from arrays ([9e504d1](https://github.com/drawnepicenter/leximaven/commit/9e504d1)), closes [#4](https://github.com/drawnepicenter/leximaven/issues/4) 190 | * **themes:** undefined/connector printing with label ([af53971](https://github.com/drawnepicenter/leximaven/commit/af53971)) 191 | * **urban:** addtl args don't include 'urban' ([ac11277](https://github.com/drawnepicenter/leximaven/commit/ac11277)) 192 | * **urban-hyphen:** incorrect labelling ([9388081](https://github.com/drawnepicenter/leximaven/commit/9388081)) 193 | 194 | 195 | ### Features 196 | 197 | * **tools:** arrToStr function ([1f5ea44](https://github.com/drawnepicenter/leximaven/commit/1f5ea44)) 198 | 199 | 200 | ### BREAKING CHANGES 201 | 202 | * CI: All changes listed in #5 203 | 204 | 205 | 206 | 207 | ## [0.1.4](https://github.com/drawnepicenter/leximaven/compare/v0.1.3...v0.1.4) (2016-08-03) 208 | 209 | 210 | ### Bug Fixes 211 | 212 | * **project:** use repo dir or pkg dir ([f13cdd1](https://github.com/drawnepicenter/leximaven/commit/f13cdd1)) 213 | 214 | 215 | 216 | 217 | ## [0.1.3](https://github.com/drawnepicenter/leximaven/compare/v0.1.2...v0.1.3) (2016-08-02) 218 | 219 | 220 | ### Bug Fixes 221 | 222 | * **project:** package directory access ([2d60a1c](https://github.com/drawnepicenter/leximaven/commit/2d60a1c)) 223 | 224 | 225 | 226 | 227 | ## [0.1.2](https://github.com/drawnepicenter/leximaven/compare/v0.1.1...v0.1.2) (2016-08-02) 228 | 229 | 230 | ### Bug Fixes 231 | 232 | * **project:** add PKGDIR ([fb504d9](https://github.com/drawnepicenter/leximaven/commit/fb504d9)) 233 | 234 | 235 | 236 | 237 | ## [0.1.1](https://github.com/drawnepicenter/leximaven/compare/v0.1.0...v0.1.1) (2016-08-01) 238 | 239 | 240 | ### Bug Fixes 241 | 242 | * make glob a runtime dep ([c3b5afb](https://github.com/drawnepicenter/leximaven/commit/c3b5afb)) 243 | 244 | 245 | 246 | 247 | # 0.1.0 (2016-08-01) 248 | 249 | 250 | ### Bug Fixes 251 | 252 | * **combine:** fix tofile ([df453fa](https://github.com/drawnepicenter/leximaven/commit/df453fa)) 253 | * **config:** init timestamps and verbose output ([85f86ec](https://github.com/drawnepicenter/leximaven/commit/85f86ec)) 254 | * **config-init:** Save remain for rate-limiting ([290986a](https://github.com/drawnepicenter/leximaven/commit/290986a)) 255 | * **config-init:** Saves remaing requests for rate-limiting ([7714a4f](https://github.com/drawnepicenter/leximaven/commit/7714a4f)) 256 | * **datamuse:** fix arg parsing ([5b153f3](https://github.com/drawnepicenter/leximaven/commit/5b153f3)) 257 | * **list-themes:** filepath str replace ([5180a76](https://github.com/drawnepicenter/leximaven/commit/5180a76)) 258 | * **origin:** fix output ([b656aaf](https://github.com/drawnepicenter/leximaven/commit/b656aaf)) 259 | * **origin:** fix tofile ([5b93352](https://github.com/drawnepicenter/leximaven/commit/5b93352)) 260 | * **project:** fix rate-limiting ([c13907a](https://github.com/drawnepicenter/leximaven/commit/c13907a)) 261 | * **project:** fix tests ([c9517e5](https://github.com/drawnepicenter/leximaven/commit/c9517e5)) 262 | * **project:** init before CI ([41e66f8](https://github.com/drawnepicenter/leximaven/commit/41e66f8)) 263 | * **project:** yargs install ([4402f7e](https://github.com/drawnepicenter/leximaven/commit/4402f7e)) 264 | * **rbrain-info:** remove maxResults ([d9736d4](https://github.com/drawnepicenter/leximaven/commit/d9736d4)) 265 | * **rbrain-rhyme:** parsing values ([2b342b0](https://github.com/drawnepicenter/leximaven/commit/2b342b0)) 266 | * **rhymebrain:** rate limiting output ([2a13efe](https://github.com/drawnepicenter/leximaven/commit/2a13efe)) 267 | * **rhymebrain:** rate-limiting and result output ([5b98c47](https://github.com/drawnepicenter/leximaven/commit/5b98c47)) 268 | * **rhymebrain:** remove debug console.log ([c82c8a0](https://github.com/drawnepicenter/leximaven/commit/c82c8a0)) 269 | * **urban:** fix args ([59877f8](https://github.com/drawnepicenter/leximaven/commit/59877f8)) 270 | * **wordmap:** change bin to build ([6b0ed9c](https://github.com/drawnepicenter/leximaven/commit/6b0ed9c)) 271 | 272 | 273 | ### Features 274 | 275 | * **dmuse:** add metrics, pull into subcmds ([e0b2483](https://github.com/drawnepicenter/leximaven/commit/e0b2483)) 276 | * **project:** datamuse subcmds and add rate-limiting ([94cb7f6](https://github.com/drawnepicenter/leximaven/commit/94cb7f6)) 277 | * **project:** url in serialized output file ([2fc81d8](https://github.com/drawnepicenter/leximaven/commit/2fc81d8)) 278 | * add api usage option ([67ff191](https://github.com/drawnepicenter/leximaven/commit/67ff191)) 279 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | As of right now, I am the only one contributing to this project. I welcome you to open issues and submit pull requests so that leximaven can be even better. 4 | 5 | ### Build process & Development Cycle 6 | 7 | - Build is managed with npm scripts. [redrun](https://github.com/coderaiser/redrun) is my task runner. Here are the tasks: 8 | 9 | - **bin** - transpiles src into bin 10 | - **bump** - after release task, pushes version to repo and publishes npm package 11 | - **lint** - fixes stylistic issues in src folder 12 | - **release** - uses standard-version to update the CHANGELOG and modify the version in package.json 13 | - **watch** - watches src directory for changes and automatically compiles to bin folder 14 | 15 | ### Changelog & Versioning 16 | 17 | - leximaven uses the [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md) format. [commitizen](http://commitizen.github.io/cz-cli/) automates this formatting. 18 | - There is no development branch on top of master, so the workflow is clean and simple. [git town](http://www.git-town.com/) helps automate this workflow. 19 | - [standard-version](https://github.com/conventional-changelog/standard-version) automates [semantic versioning](http://semver.org/spec/v2.0.0.html) and changelog generation. 20 | - See the [CHANGELOG](https://github.com/drawnepicenter/leximaven/blob/master/CHANGELOG.md) for progress. 21 | 22 | ### Coding Style & Linting 23 | 24 | This project adheres to [standard](https://github.com/feross/standard) formatting rules. 25 | 26 | ### Testing 27 | 28 | I was using Mocha, Chai, and Sinon for testing. At the moment there are no tests. 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | **Copyright (c) 2017-2019 Andrew Prentice** 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # leximaven 2 | 3 | [![Build Status](https://travis-ci.org/scalarwaves/leximaven.svg?branch=master)](https://travis-ci.org/scalarwaves/leximaven) [![npm version](https://badge.fury.io/js/leximaven.svg)](https://badge.fury.io/js/leximaven) [![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) 4 | 5 | ## Introduction 6 | 7 | leximaven is a powerful tool for searching word-related APIs from the command line. It can fetch acronyms, bi-gram phrases, definitions, etymologies, example uses, hyphenation, offensive word flags, portmanteaus, pronunciations (Arpabet & IPA), related words, rhymes, slang, syllable stress and count, and more. See the [wiki](https://github.com/drawnepicenter/leximaven/wiki) for more info. 8 | 9 | ## Platform 10 | 11 | Looking for testers on other platforms. Developed and tested on Linux. Works on Windows, see [Windows](#windows-installation) below. 12 | 13 | Supported Node.js versions: 14 | 15 | - Typescript 16 | - 12.x 17 | - 11.x 18 | - 10.x 19 | - 8.x 20 | 21 | ## Install 22 | 23 | ### Linux installation 24 | 25 | To initialize the config file and load themes, your NODE_PATH environment variable must point to the **node_modules** directory of the Node.js installation. You can set this path automatically like this: 26 | 27 | export NP=$(which node) 28 | export BP=${NP%bin/node} #this replaces the string '/bin/node' 29 | export LP="${BP}lib/node_modules" 30 | export NODE_PATH="$LP" 31 | 32 | Provided these lines are towards the end of the shell initialization file (at least after any NVM stuff) this should work for a system installation of Node.js and [nvm](https://github.com/creationix/nvm). 33 | 34 | - Put your [Wordnik API key](http://developer.wordnik.com/) into an environment variable **WORDNIK** 35 | 36 | Add all of this to .bashrc, .zshrc, etc. then: 37 | 38 | npm install -g leximaven 39 | leximaven config init 40 | 41 | ### Windows installation 42 | 43 | I highly recommend using [nodist](https://github.com/marcelklehr/nodist) to install Node.js on Windows. It automatically sets %NODE_PATH% for you, though you may have to edit it to make sure it doesn't contain itself (i.e. C:\...\...\node_modules;%NODE_PATH%). If you install Node.js manually, `npm install --global leximaven` will install the package in C:\Users\username\AppData\Roaming\npm\node_modules. And if you just do `npm install leximaven` then it will install the package to a subfolder of the Node.js installation, but that won't be the NODE_PATH folder unless you manually set it. Either way, you're going to have to mess around with Windows environment variables to get it to work. And don't forget to put your [Wordnik API key](http://developer.wordnik.com/) into an environment variable **WORDNIK** 44 | 45 | As for getting the ANSI color escape codes to work, [Cmder](http://cmder.net/) seems to be the easiest way. It doesn't install a full linux environment like Cygwin, but you can still use some linux commands like **which**, **cat**, and **ls**. 46 | 47 | ## Usage 48 | 49 | leximaven has a built-in help system for CLI parameters and options. Access it with `leximaven -h|--help [command] [subcommand]`. There is also the [wiki](https://github.com/drawnepicenter/leximaven/wiki). 50 | 51 | Here are some examples: 52 | 53 | // Get definitions for 'catharsis' 54 | leximaven wordnik define catharsis 55 | 56 | // Get antonyms for 'noise' 57 | leximaven wordnik relate --canon --type antonym noises 58 | 59 | // Pronounce 'quixotic' 60 | leximaven wordnik pronounce quixotic 61 | 62 | // Get etymology for 'special' 63 | leximaven wordnik origin special 64 | 65 | // Get words that sound like 'blue' 66 | leximaven datamuse get sl=blue 67 | 68 | // Get slang/colloquialisms for 'diesel' 69 | leximaven urban diesel 70 | 71 | // Get anagrams with at least 2 letters in each word and a maximum of 3 words 72 | // per anagram using short form flags and exporting to JSON 73 | leximaven anagram -n2 -w3 -o anagrams.json toomanysecrets 74 | 75 | // Get a wordmap for 'ubiquity' 76 | leximaven wordmap ubiquity 77 | 78 | ## Resources 79 | 80 | The following links can help you use leximaven or perform related tasks. 81 | 82 | - [alex](https://github.com/wooorm/alex) Checks your writing for words or phrasings that might offend someone 83 | - [proselint](https://github.com/amperser/proselint) checks your writing style and has plugins for multiple editors 84 | - [retext](https://github.com/wooorm/retext) is a framework for natural language processing 85 | - [write-good](https://github.com/btford/write-good) Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too 86 | - ISO 639-1 [Language Codes](http://www.loc.gov/standards/iso639-2/php/English_list.php) for Rhymebrain functions 87 | - [Arpabet](http://en.wikipedia.org/wiki/Arpabet) phoneme list and [IPA](http://en.wikipedia.org/wiki/Help:IPA_for_English) equivalents 88 | - [Dewey Decimal Classes](http://en.wikipedia.org/wiki/List_of_Dewey_Decimal_classes) for acronyms 89 | - Browse Datamuse's Onelook [dictionaries](http://www.onelook.com/?d=all_gen), use its [dictionary lookup](http://www.onelook.com/), [thesaurus/reverse lookup](http://www.onelook.com/thesaurus/), and [RhymeZone](http://www.rhymezone.com/) 90 | 91 | ## Contributing 92 | 93 | See [CONTRIBUTING](https://github.com/drawnepicenter/leximaven/blob/master/CONTRIBUTING.md). 94 | 95 | ## License 96 | 97 | MIT :copyright: 2017-2019 Andrew Prentice 98 | 99 | ## Powered by 100 | 101 | Acronym Server, Datamuse, Onelook, Rhymebrain, Urban Dictionary, and Wordnik 102 | 103 | ## Extras 104 | 105 | ### Prose 106 | 107 | For fun, read some of my [prose](https://github.com/drawnepicenter/prose#readme)... 108 | 109 | ### Take Command 110 | 111 | See [take-command](https://github.com/drawnepicenter/take-command). 112 | -------------------------------------------------------------------------------- /bin/commands/acronym.js: -------------------------------------------------------------------------------- 1 | 'use strict';var themes=require('../themes');var tools=require('../tools');var _=require('lodash');var chalk=require('chalk');var http=require('good-guy-http')();var noon=require('noon');var xml2js=require('xml2js');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='acronym ';exports.aliases=['acro','ac'];exports.desc='Acronyms';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Acronyms');var acronym=argv.acronym.toUpperCase();var url='http://acronyms.silmaril.ie/cgi-bin/xaa?'+argv.acronym;var tofile={type:'acronym',source:'http://acronyms.silmaril.ie',url:url};var ctstyle=_.get(chalk,theme.content.style);http({url:url},function(error,response){if(!error&&response.statusCode===200){var body=response.body;var parser=new xml2js.Parser();parser.parseString(body,function(err,result){if(!err){var found=result.acronym.found[0];var count=found.$;if(count.n==='0'){console.log(ctstyle('Found 0 acronyms for '+acronym+'.'));}else{console.log(ctstyle('Found '+count.n+' acronyms for '+acronym+':'));var list=found.acro;for(var i=0;i<=list.length-1;i++){var item=list[i];process.stdout.write(ctstyle(''+item.expan));tofile[['expansion'+i]]=item.expan[0];var comm=item.comment[0];if(comm!==''){if(comm.a){var comment=comm.a[0];process.stdout.write(ctstyle(' - '+comment._+' - '+comment.$.href));tofile[['comment'+i]]=comment._;tofile[['url'+i]]=comment.$.href;}else{process.stdout.write(ctstyle(' - '+comm));tofile[['comment'+i]]=item.comment[0];}}console.log(ctstyle(' - DDC: '+item.$.dewey));tofile[['DDC'+i]]=item.$.dewey;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);}}else{throw new Error(err);}});}else{throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);}});}; -------------------------------------------------------------------------------- /bin/commands/completion.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint no-unused-vars:0, no-unused-expressions:0 */var yargs=require('yargs');exports.command='completion';exports.aliases=['comp'];exports.desc='Print shell completion script';exports.builder={};exports.handler=function(argv){yargs.showCompletionScript().argv;}; -------------------------------------------------------------------------------- /bin/commands/configuration.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint no-unused-vars: 0 */exports.command='configuration ';exports.aliases=['conf','config'];exports.desc='Configuration tasks';exports.builder=function(yargs){return yargs.commandDir('configuration');};exports.handler=function(argv){}; -------------------------------------------------------------------------------- /bin/commands/configuration/get.js: -------------------------------------------------------------------------------- 1 | 'use strict';var themes=require('../../themes');var tools=require('../../tools');var chalk=require('chalk');var dot=require('dot-prop');var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='get ';exports.aliases=['g'];exports.desc='Retrieve a config value';exports.builder={};exports.handler=function(argv){var key=argv.key;var value=null;tools.checkConfig(CFILE);var config=noon.load(CFILE);var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Configuration');if(dot.has(config,key)){value=/\./i.test(key)?dot.get(config,key):config[key];}else{throw new Error('Option '+key+' not found.');}console.log('Option '+chalk.white.bold(key)+' is '+chalk.white.bold(value)+'.');}; -------------------------------------------------------------------------------- /bin/commands/configuration/init.js: -------------------------------------------------------------------------------- 1 | 'use strict';var themes=require('../../themes');var chalk=require('chalk');var fs=require('fs');var noon=require('noon');var os=require('os');var CFILE=process.env.HOME+'/.leximaven.noon';var PKGDIR=process.env.NODE_PATH+'/leximaven/';exports.command='init';exports.aliases=['i'];exports.desc='Initialize config file';exports.builder={force:{alias:'f',desc:'Force overwriting configuration file',default:false,type:'boolean'}};exports.handler=function(argv){var obj=null;var configExists=null;var dirExists=null;try{fs.statSync('default.config.noon');configExists=true;}catch(e){if(e.code==='ENOENT')configExists=false;}if(configExists){obj=noon.load('default.config.noon');}else{try{fs.statSync(PKGDIR);dirExists=true;}catch(e){if(e.code==='ENOENT'){dirExists=false;}}if(dirExists){obj=noon.load(PKGDIR+'default.config.noon');}else{throw new Error('Package dir not found, set NODE_PATH per documentation.');}}obj.dmuse.date.stamp=new Date().toJSON();obj.onelook.date.stamp=new Date().toJSON();obj.rbrain.date.stamp=new Date().toJSON();obj.wordnik.date.stamp=new Date().toJSON();var fileExists=null;try{fs.statSync(CFILE);fileExists=true;}catch(e){if(e.code==='ENOENT'){fileExists=false;}}if(fileExists){if(argv.f){var _config=noon.load(CFILE);obj.dmuse.date.stamp=_config.dmuse.date.stamp;obj.dmuse.date.remain=_config.dmuse.date.remain;obj.onelook.date.stamp=_config.onelook.date.stamp;obj.onelook.date.remain=_config.onelook.date.remain;obj.rbrain.date.stamp=_config.rbrain.date.stamp;obj.rbrain.date.remain=_config.rbrain.date.remain;obj.wordnik.date.stamp=_config.wordnik.date.stamp;obj.wordnik.date.remain=_config.wordnik.date.remain;noon.save(CFILE,obj);console.log('Overwrote '+chalk.white.bold(CFILE)+'.');}else{console.log('Using configuration at '+chalk.white.bold(CFILE)+'.');}}else if(!fileExists){noon.save(CFILE,obj);console.log('Created '+chalk.white.bold(CFILE)+'.');}var config=noon.load(CFILE);var theme=themes.loadTheme(config.theme);if(os.platform()==='windows')themes.label(theme,'right','Notice','Please see the README for best user experience on Windows.');if(argv.v){themes.label(theme,'down','Configuration');console.log('Your current configuration is:');console.log(noon.stringify(config,{indent:2,align:true,maxalign:32,sort:true,colors:true}));console.log('');}}; -------------------------------------------------------------------------------- /bin/commands/configuration/set.js: -------------------------------------------------------------------------------- 1 | 'use strict';var themes=require('../../themes');var tools=require('../../tools');var chalk=require('chalk');var dot=require('dot-prop');var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='set ';exports.aliases=['s'];exports.desc='Set a config value';exports.builder={};exports.handler=function(argv){var key=argv.key;var value=argv.value;value=tools.checkBoolean(value);tools.checkConfig(CFILE);var config=noon.load(CFILE);var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Configuration');if(dot.has(config,key)){if(/\./i.test(key)){if(/^\w*\.date/i.test(key)){throw new Error("API limits hardcoded, can't set this key.");}else{dot.set(config,key,value);}}else{config[key]=value;}}else{throw new Error('Option '+key+' not found.');}noon.save(CFILE,config);console.log('Set option '+chalk.white.bold(key)+' to '+chalk.white.bold(value)+'.');}; -------------------------------------------------------------------------------- /bin/commands/datamuse.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint no-unused-vars: 0 */exports.command='datamuse ';exports.aliases=['dmuse','dm'];exports.desc='Datamuse tasks';exports.builder=function(yargs){return yargs.commandDir('datamuse');};exports.handler=function(argv){}; -------------------------------------------------------------------------------- /bin/commands/datamuse/get.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var chalk=require('chalk');var http=require('good-guy-http')();var noon=require('noon');var ora=require('ora');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='get ';exports.desc='Datamuse query';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},max:{alias:'m',desc:'Maximum number of results, 1 to 1000',default:5,type:'number'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.dmuse.date.stamp);var hours=df.differenceInHours(new Date(),stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitDmuse(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={dmuse:{max:argv.m}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Datamuse');var ccont=[];ccont.push(argv.condition);if(argv._.length>1){for(var i=0;i<=argv._.length-1;i++){if(argv._[i]!=='datamuse'&&argv._[i]!=='dmuse'&&argv._[i]!=='dm'&&argv._[i]!=='get'){ccont.push(argv._[i]);}}}var prefix='http://api.datamuse.com/words?';var conditions='max='+config.dmuse.max+'&';for(var _i=0;_i<=ccont.length-1;_i++){conditions=conditions+'&'+ccont[_i];}var url=''+prefix+conditions;url=encodeURI(url);var tags={n:'noun',adj:'adjective',adv:'adverb',syn:'synonym'};var tofile={type:'datamuse',source:'http://datamuse.com/api',url:url};var ctstyle=_.get(chalk,theme.content.style);var spinner=ora({text:''+chalk.bold.cyan('Loading results...'),spinner:'dots8',color:'yellow'});http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.dmuse.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var resp=JSON.parse(response.body);spinner.stop();spinner.clear();for(var _i2=0;_i2<=resp.length-1;_i2++){var item=resp[_i2];themes.label(theme,'right','Match',item.word+' ');tofile[['match'+_i2]]=item.word;if(item.tags!==undefined&&item.tags!==[]){themes.label(theme,'right','Tag');for(var j=0;j<=item.tags.length-1;j++){if(j===item.tags.length-1){process.stdout.write(ctstyle(''+tags[item.tags[j]]));tofile[['tags'+j]]=tags[item.tags[j]];}else process.stdout.write(ctstyle(tags[item.tags[j]]+', '));}console.log('');}}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, reset usage limits.\n'+config.dmuse.date.remain+'/'+config.dmuse.date.limit+' requests remaining today.'):console.log(config.dmuse.date.remain+'/'+config.dmuse.date.limit+' requests remaining today, will reset in '+(23-hours)+' hours, '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached today\'s usage limit of '+config.dmuse.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/datamuse/info.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0, no-unused-vars:0 */var tools=require('../../tools');var chalk=require('chalk');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='info';exports.desc='Datamuse metrics';exports.builder={};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var url='http://api.datamuse.com/metrics';http({url:url},function(error,response){if(!error&&response.statusCode===200){var body=JSON.parse(response.body);var version=body[0];var qps=body[1];var sugf=body[2];var sugn=body[3];var wordf=body[4];var wordn=body[5];console.log(chalk.white('Current queries per second (v'+Math.round(version.value*100)/100.0+'): '+Math.round(qps.value*100)/100.0));console.log(chalk.white('Latency (/words): '+Math.round(wordf.value*100000)/100.0+' ms (median), '+Math.round(wordn.value*100000)/100.0+' ms (99 %ile)'));console.log(chalk.white('Latency (/sug): '+Math.round(sugf.value*100000)/100.0+' ms (median), '+Math.round(sugn.value*100000)/100.0+' ms (99 %ile)'));}else{throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);}});var limit=config.dmuse.date.limit;var remain=config.dmuse.date.remain;var stamp=new Date(config.dmuse.date.stamp);var hours=df.differenceInHours(new Date(),stamp);var minutes=df.differenceInMinutes(new Date(),stamp);console.log(chalk.white(remain+'/'+limit+' requests remain today, will reset in '+(23-hours)+' hours, '+(59-minutes)+' minutes.'));}; -------------------------------------------------------------------------------- /bin/commands/list.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint no-unused-vars: 0 */var themes=require('../themes');exports.command='list';exports.aliases=['ls','themes'];exports.desc='Get a list of installed themes';exports.builder={};exports.handler=function(argv){var list=themes.getThemes();for(var i=0;i<=list.length-1;i++){var currentTheme=themes.loadTheme(list[i]);var sample='Morbi ornare pulvinar metus, non faucibus arcu ultricies non.';themes.label(currentTheme,'down',list[i],sample);}}; -------------------------------------------------------------------------------- /bin/commands/onelook.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../themes');var tools=require('../tools');var _=require('lodash');var df=require('date-fns');var chalk=require('chalk');var http=require('good-guy-http')();var noon=require('noon');var xml2js=require('xml2js');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='onelook ';exports.aliases=['one','ol'];exports.desc='Onelook definitions';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},links:{alias:'l',desc:'Include resource links',default:false,type:'boolean'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var checkStamp=tools.limitOnelook(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];var stamp=new Date(config.onelook.date.stamp);var hours=df.differenceInHours(new Date(),stamp);var minutes=df.differenceInMinutes(new Date(),stamp);if(proceed){var userConfig={onelook:{links:argv.l}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Onelook');var acont=[];acont.push(argv.word);if(argv._.length>1){for(var i=0;i<=argv._.length-1;i++){if(argv._[i]!=='onelook'&&argv._[i]!=='one'&&argv._[i]!=='ol')acont.push(argv._[i]);}}var url='http://onelook.com/?xml=1&w='+acont.join('+');url=encodeURI(url);var tofile={type:'onelook',source:'http://www.onelook.com',url:url};var ctstyle=_.get(chalk,theme.content.style);http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.onelook.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var body=response.body;var parser=new xml2js.Parser();parser.parseString(body,function(err,result){if(!err){var resp=result.OLResponse;var phrase=resp.OLPhrases[0];var similar=resp.OLSimilar[0];var quickdef=resp.OLQuickDef;var resources=resp.OLRes;themes.label(theme,'down','Definition');if(Array.isArray(quickdef)&&quickdef.length>1){for(var _i=0;_i<=quickdef.length-1;_i++){var item=quickdef[_i];item=item.replace(/<|>|\n|\/i/g,'');item=item.replace(/i"/g,'"');console.log(ctstyle(item));tofile[['definition'+_i]]=item;}}else{var definition=quickdef[0].replace(/<|>|\n|\/i/g,'');console.log(ctstyle(definition));tofile.definition=definition;}if(phrase){var phrases=phrase.replace(/\n/g,'');themes.label(theme,'down','Phrases',phrases);tofile.phrase=phrases;}if(similar){var sim=similar.replace(/\n/g,'');themes.label(theme,'down','Similar',sim);tofile.sim=sim;}if(config.onelook.links){themes.label(theme,'down','Resources');for(var _i2=0;_i2<=resources.length-1;_i2++){var _item=resources[_i2];var res=tools.arrToStr(_item.OLResName).replace(/\n/g,'');var link=tools.arrToStr(_item.OLResLink).replace(/\n/g,'');var home=tools.arrToStr(_item.OLResHomeLink).replace(/\n/g,'');themes.label(theme,'right',res,link);tofile[['res'+_i2]]=res;tofile[['link'+_i2]]=link;tofile[['home'+_i2]]=home;}}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage){if(reset){console.log('Timestamp expired, reset usage limits.');console.log(config.onelook.date.remain+'/'+config.onelook.date.limit+' requests remaining today.');}else console.log(config.onelook.date.remain+'/'+config.onelook.date.limit+' requests remaining today, will reset in '+(23-hours)+' hours, '+(59-minutes)+' minutes.');}}else{throw new Error(err);}});}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached today\'s usage limit of '+config.onelook.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/random.js: -------------------------------------------------------------------------------- 1 | 'use strict';var rand=require('random-word');exports.command='random';exports.aliases=['rand','rw'];exports.desc='Get a random word';exports.builder={};exports.handler=function(argv){console.log(rand());}; -------------------------------------------------------------------------------- /bin/commands/rhymebrain.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint no-unused-vars: 0 */exports.command='rhymebrain ';exports.aliases=['rbrain','rb'];exports.desc='Rhymebrain operations';exports.builder=function(yargs){return yargs.commandDir('rhymebrain');};exports.handler=function(argv){}; -------------------------------------------------------------------------------- /bin/commands/rhymebrain/combine.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='combine ';exports.aliases=['comb','portmanteau'];exports.desc='Rhymebrain portmanteaus';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},lang:{alias:'l',desc:'ISO 639-1 language code',default:'en',type:'string'},max:{alias:'m',desc:'Max results to return',default:5,type:'number'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.rbrain.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitRbrain(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={rbrain:{combine:{lang:argv.l,max:argv.m}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Rhymebrain');var query=argv.query;var task='Portmanteaus';var prefix='http://rhymebrain.com/talk?function=get';var uri=''+prefix+task+'&word='+query+'&';var pcont=[];pcont.push('lang='+config.rbrain.combine.lang+'&');pcont.push('maxResults='+config.rbrain.combine.max+'&');var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);themes.label(theme,'down',task);var tofile={type:'portmanteau',source:'http://rhymebrain.com',url:url};http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.rbrain.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);for(var i=0;i<=list.length-1;i++){var item=list[i];themes.label(theme,'right',item.source,item.combined);tofile[['set'+i]]=item.source;tofile[['portmanteau'+i]]=item.combined;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, reset usage limits.\n'+config.rbrain.date.remain+'/'+config.rbrain.date.limit+' requests remaining this hour.'):console.log(config.rbrain.date.remain+'/'+config.rbrain.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.rbrain.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/rhymebrain/info.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var chalk=require('chalk');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='info ';exports.desc='Rhymebrain word info';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},lang:{alias:'l',desc:'ISO 639-1 language code',default:'en',type:'string'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.rbrain.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitRbrain(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={rbrain:{info:{lang:argv.l}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Rhymebrain');var word=argv.word;var task='WordInfo';var prefix='http://rhymebrain.com/talk?function=get';var uri=''+prefix+task+'&word='+word+'&lang='+config.rbrain.info.lang;var url=encodeURI(uri);themes.label(theme,'down','Word Info');var tofile={type:'word info',source:'http://rhymebrain.com',url:url};var ctstyle=_.get(chalk,theme.content.style);http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.rbrain.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var info=JSON.parse(response.body);themes.label(theme,'right','Arpabet',info.pron);themes.label(theme,'right','IPA',info.ipa);themes.label(theme,'right','Syllables',info.syllables);tofile.arpabet=info.pron;tofile.ipa=info.ipa;tofile.syllables=info.syllables;var flags=[];if(info.flags.match(/a/)){flags.push(ctstyle('['+chalk.red.bold('Offensive')+']'));tofile.offensive=true;}if(info.flags.match(/b/)){flags.push(ctstyle('[Found in dictionary]'));tofile.dict=true;}if(info.flags.match(/c/)){flags.push(ctstyle('[Trusted pronunciation, not generated]'));tofile.trusted=true;}themes.label(theme,'right','Word Flags',flags.join(''));if(argv.o)tools.outFile(argv.o,argv.f,tofile);reset?console.log('Timestamp expired, reset usage limits.\n'+config.rbrain.date.remain+'/'+config.rbrain.date.limit+' requests remaining this hour.'):console.log(config.rbrain.date.remain+'/'+config.rbrain.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.rbrain.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/rhymebrain/rhyme.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var chalk=require('chalk');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='rhyme ';exports.aliases=['rh'];exports.desc='Rhymebrain rhymes';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},lang:{alias:'l',desc:'ISO 639-1 language code',default:'en',type:'string'},max:{alias:'m',desc:'Max results to return',default:5,type:'number'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.rbrain.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitRbrain(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={rbrain:{rhyme:{lang:argv.l,max:argv.m}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Rhymebrain');var word=argv.word;var task='Rhymes';var prefix='http://rhymebrain.com/talk?function=get';var uri=''+prefix+task+'&word='+word+'&';var pcont=[];pcont.push('lang='+config.rbrain.rhyme.lang+'&');pcont.push('maxResults='+config.rbrain.rhyme.max+'&');var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);var tofile={type:'rhyme',source:'http://rhymebrain.com',url:url};var ctstyle=_.get(chalk,theme.content.style);http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.rbrain.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);var lcont=[];for(var i=0;i<=list.length-1;i++){lcont.push(list[i].word);}lcont.sort(function(a,b){if(ab)return 1;return 0;});var rcont=[];for(var j=0;j<=lcont.length-1;j++){var item=lcont[j];rcont.push(ctstyle(item));item.score>=300?tofile[['hiscore'+j]]=item:tofile[['rhyme'+j]]=item;}rcont.sort();themes.label(theme,'right',task,rcont.join(', '));if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, reset usage limits.\n'+config.rbrain.date.remain+'/'+config.rbrain.date.limit+' requests remaining this hour.'):console.log(config.rbrain.date.remain+'/'+config.rbrain.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.rbrain.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/urban.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../themes');var tools=require('../tools');var _=require('lodash');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='urban ';exports.aliases=['urb','slang'];exports.desc='Urban Dictionary definitions';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit number of results',default:5,type:'number'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var userConfig={urban:{limit:argv.l}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Urban Dictionary');var ucont=[];ucont.push(argv.query);if(argv._.length>1){for(var i=0;i<=argv._.length-1;i++){if(argv._[i]!=='urban'&&argv._[i]!=='urb'&&argv._[i]!=='slang')ucont.push(argv._[i]);}}var words='';if(ucont.length>1){words=ucont.join('+');}else{words=ucont[0];}var url='http://api.urbandictionary.com/v0/define?term='+words;url=encodeURI(url);var tofile={type:'urban',source:'http://www.urbandictionary.com',url:url};http({url:url},function(error,response){if(!error&&response.statusCode===200){var body=JSON.parse(response.body);var limit=config.urban.limit;var list=body.list.slice(0,limit);for(var _i=0;_i<=list.length-1;_i++){var result=list[_i];themes.label(theme,'down','Definition',result.definition);tofile[['definition'+_i]]=result.definition;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);}else{throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);}});}; -------------------------------------------------------------------------------- /bin/commands/wordmap.js: -------------------------------------------------------------------------------- 1 | 'use strict';var themes=require('../themes');var tools=require('../tools');var _=require('lodash');var child=require('child_process');var fs=require('fs');var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='wordmap ';exports.aliases=['map','wm'];exports.desc='Maps of word info';exports.builder={limit:{alias:'l',desc:'Limits the number of results',default:1,type:'number'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'}};exports.handler=function(argv){tools.checkConfig(CFILE);var config=noon.load(CFILE);var userConfig={wordmap:{limit:argv.l}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordmap');var word=argv.word;var l=argv.l;var bin='';var dirExists=null;try{fs.statSync('bin/leximaven.js');dirExists=true;}catch(e){if(e.code==='ENOENT')dirExists=false;}dirExists?bin='bin/leximaven.js':bin=process.env.NODE_PATH+'/leximaven/bin/leximaven.js';child.spawnSync('node',[bin,'rbrain','combine','-m'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'rbrain','info',''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'rbrain','rhyme','-m'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','define','-l'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','example','-l'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','hyphen',''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','origin',''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','phrase','-l'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','pronounce','-l'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'wordnik','relate','-l'+l,''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'acronym',''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'dmuse','-m'+l,'ml='+word],{stdio:'inherit'});child.spawnSync('node',[bin,'onelook',''+word],{stdio:'inherit'});child.spawnSync('node',[bin,'urban','-l'+l,''+word],{stdio:'inherit'});// child.spawnSync('node', [bin, 'anagram', `-t${l}`, `${word}`], { stdio: 'inherit' }) 2 | }; -------------------------------------------------------------------------------- /bin/commands/wordnik.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint no-unused-vars: 0 */exports.command='wordnik ';exports.aliases=['wnik','wn'];exports.desc='Wordnik tasks';exports.builder=function(yargs){return yargs.commandDir('wordnik');};exports.handler=function(argv){}; -------------------------------------------------------------------------------- /bin/commands/wordnik/define.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var chalk=require('chalk');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='define ';exports.aliases=['def'];exports.desc='Wordnik definitions';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit number of results',default:5,type:'number'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'},defdict:{alias:'d',desc:"CSV list of dictionaries or 'all'",default:'all',type:'string'},part:{alias:'p',desc:'CSV list of parts of speech. See http://developer.wordnik.com/docs.html for list of parts.',default:'',type:'string'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{define:{canon:argv.c,limit:argv.l,defdict:argv.d,part:argv.p}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='definitions';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+config.wordnik.define.canon+'&');pcont.push('sourceDictionaries='+config.wordnik.define.defdict+'&');pcont.push('includeRelated=false&');pcont.push('includeTags=false&');pcont.push('limit='+config.wordnik.define.limit+'&');pcont.push('partOfSpeech='+config.wordnik.define.part+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);var tofile={type:'definition',source:'http://www.wordnik.com'};var cstyle=_.get(chalk,theme.connector.style);var ctstyle=_.get(chalk,theme.content.style);var uline=_.get(chalk,theme.content.style+'.underline');var conn=cstyle(theme.connector.str);http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);for(var i=0;i<=list.length-1;i++){var item=list[i];var icont=[];icont.push(ctstyle(item.text+' '));icont.push(uline(item.partOfSpeech));icont.push(conn);icont.push(ctstyle(item.sourceDictionary));themes.label(theme,'right','Definition',icont.join(''));tofile[['text'+i]]=item.text;tofile[['deftype'+i]]=item.partOfSpeech;tofile[['source'+i]]=item.sourceDictionary;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, not decrementing usage.\n'+config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour.'):console.log(config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.wordnik.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/wordnik/example.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='example ';exports.aliases=['ex'];exports.desc='Wordnik examples';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit number of results',default:5,type:'number'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'},skip:{alias:'k',desc:'Number of results to skip',default:0,type:'number'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{example:{canon:argv.c,limit:argv.l,skip:argv.k}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='examples';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+config.wordnik.example.canon+'&');pcont.push('includeDuplicates=false&');pcont.push('limit='+config.wordnik.example.limit+'&');!config.wordnik.example.skip?pcont.push('skip=0&'):pcont.push('skip='+config.wordnik.example.skip+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);var tofile={type:'example',source:'http://www.wordnik.com'};http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var body=JSON.parse(response.body);var list=body.examples;for(var i=0;i<=list.length-1;i++){var item=list[i];themes.label(theme,'right','Example',item.text);tofile[['example'+i]]=item.text;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, not decrementing usage.\n'+config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour.'):console.log(config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.wordnik.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/wordnik/hyphen.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var chalk=require('chalk');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='hyphen ';exports.aliases=['hyphenate','hy'];exports.desc='Wordnik hyphenations';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit number of results',default:5,type:'number'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'},dict:{alias:'d',desc:'Source dictionary ahd, century, wiktionary, webster, wordnet',default:'all',type:'string'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{hyphen:{canon:argv.c,dict:argv.d,limit:argv.l}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='hyphenation';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+config.wordnik.hyphen.canon+'&');if(argv.d!=='all')pcont.push('sourceDictionary='+config.wordnik.hyphen.dict+'&');pcont.push('limit='+config.wordnik.hyphen.limit+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);var tofile={type:'hyphenation',source:'http://www.wordnik.com'};var ctstyle=_.get(chalk,theme.content.style);http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);var hcont=[];for(var i=0;i<=list.length-1;i++){var item=list[i];if(item.type==='stress'){hcont.push(''+chalk.red.bold(item.text));tofile[['stress'+i]]=item.text;}else if(item.type==='secondary stress'){hcont.push(ctstyle(item.text));tofile[['secondary'+i]]=item.text;}else{hcont.push(ctstyle(item.text));tofile[['syllable'+i]]=item.text;}if(i';exports.aliases=['or','etymology'];exports.desc='Wordnik etymologies';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{origin:{canon:argv.c}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='etymologies';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+config.wordnik.origin.canon+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);var parser=new xml2js.Parser();var tofile={type:'etymology',source:'http://www.wordnik.com'};var ctstyle=_.get(chalk,theme.content.style);http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var resp=JSON.parse(response.body);var origin=resp[0];parser.parseString(origin,function(err,result){if(!err){var root=result.ety;var content=root._;var ets=root.ets;ets=ets.join(', ');themes.label(theme,'right','Etymology',ctstyle(content+' '+ets));tofile.etymology=content;tofile.origin=ets;}else{throw new Error(err);}});if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, not decrementing usage.\n'+config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour.'):console.log(config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.wordnik.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/wordnik/phrase.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='phrase ';exports.aliases=['ph','ngram'];exports.desc='Wordnik bi-gram phrases';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit number of results',default:5,type:'number'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'},weight:{alias:'w',desc:'Minimum weighted mutual info',default:13,type:'number'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{phrase:{canon:argv.c,limit:argv.l,weight:argv.w}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='phrases';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+argv.c+'&');pcont.push('limit='+argv.l+'&');pcont.push('wlmi='+argv.w+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);themes.label(theme,'down','Bi-gram phrases');var tofile={type:'phrase',source:'http://www.wordnik.com'};http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);for(var i=0;i<=list.length-1;i++){var item=list[i];console.log(item.gram1+' '+item.gram2);tofile[['agram'+i]]=item.gram1;tofile[['bgram'+i]]=item.gram2;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, not decrementing usage.\n'+config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour.'):console.log(config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.wordnik.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/wordnik/pronounce.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='pronounce ';exports.aliases=['pr'];exports.desc='Wordnik pronunciations';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit number of results',default:5,type:'number'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'},dict:{alias:'d',desc:'Dictionary: ahd, century, cmu, macmillan, wiktionary, webster, wordnet',default:'',type:'string'},type:{alias:'t',desc:'Type: ahd, arpabet, gcide-diacritical, ipa',default:'',type:'string'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{pronounce:{canon:argv.c,dict:argv.d,type:argv.t,limit:argv.l}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='pronunciations';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+config.wordnik.pronounce.canon+'&');if(config.wordnik.pronounce.dict!=='')pcont.push('sourceDictionary='+config.wordnik.pronounce.dict+'&');if(config.wordnik.pronounce.type!=='')pcont.push('typeFormat='+config.wordnik.pronounce.type+'&');pcont.push('limit='+config.wordnik.pronounce.limit+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);themes.label(theme,'down','Pronunciations');var tofile={type:'pronunciation',source:'http://www.wordnik.com'};tofile.word=word;http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);for(var i=0;i<=list.length-1;i++){var item=list[i];themes.label(theme,'right',word,item.raw+' - Type - '+item.rawType);tofile[['pronunciation'+i]]=item.raw;tofile[['type'+i]]=item.rawType;}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, not decrementing usage.\n'+config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour.'):console.log(config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.wordnik.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/commands/wordnik/relate.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var themes=require('../../themes');var tools=require('../../tools');var _=require('lodash');var df=require('date-fns');var http=require('good-guy-http')();var noon=require('noon');var CFILE=process.env.HOME+'/.leximaven.noon';exports.command='relate ';exports.aliases=['related','rel'];exports.desc='Wordnik related words';exports.builder={out:{alias:'o',desc:'Write cson, json, noon, plist, yaml, xml',default:'',type:'string'},force:{alias:'f',desc:'Force overwriting outfile',default:false,type:'boolean'},save:{alias:'s',desc:'Save flags to config file',default:false,type:'boolean'},limit:{alias:'l',desc:'Limit results = require(type option',default:10,type:'number'},canon:{alias:'c',desc:'Use canonical',default:false,type:'boolean'},type:{alias:'t',desc:'Relationship types to limit',default:'',type:'string'}};exports.handler=function(argv){if(process.env.WORDNIK===undefined)throw new Error('Put an API key in environment variable WORDNIK per documentation.');tools.checkConfig(CFILE);var config=noon.load(CFILE);var proceed=false;var reset=false;var stamp=new Date(config.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);var checkStamp=tools.limitWordnik(config);config=checkStamp[0];proceed=checkStamp[1];reset=checkStamp[2];if(proceed){var userConfig={wordnik:{relate:{canon:argv.c,type:argv.t,limit:argv.l}}};if(config.merge)config=_.merge({},config,userConfig);if(argv.s&&config.merge)noon.save(CFILE,config);if(argv.s&&!config.merge)throw new Error("Can't save user config, set option merge to true.");var theme=themes.loadTheme(config.theme);if(config.verbose)themes.label(theme,'down','Wordnik');var word=argv.word;var task='relatedWords';var prefix='http://api.wordnik.com:80/v4/word.json/';var apikey=process.env.WORDNIK;var uri=''+prefix+word+'/'+task+'?';var pcont=[];pcont.push('useCanonical='+config.wordnik.relate.canon+'&');if(config.wordnik.relate.type!=='')pcont.push('relationshipTypes='+config.wordnik.relate.type+'&');pcont.push('limitPerRelationshipType='+config.wordnik.relate.limit+'&');pcont.push('api_key='+apikey);var rest=pcont.join('');var url=''+uri+rest;url=encodeURI(url);themes.label(theme,'down','Related words');var tofile={type:'related words',source:'http://www.wordnik.com'};tofile.word=word;http({url:url},function(error,response){if(!error&&response.statusCode===200){if(response.headers['x-gg-state']==='cached'){config.wordnik.date.remain++;noon.save(CFILE,config);if(config.usage)console.log('Cached response, not decrementing usage.');}var list=JSON.parse(response.body);for(var i=0;i<=list.length-1;i++){var item=list[i];themes.label(theme,'right',item.relationshipType,''+item.words.join(', '));tofile[['type'+i]]=item.relationshipType;tofile[['words'+i]]=item.words.join(', ');}if(argv.o)tools.outFile(argv.o,argv.f,tofile);if(config.usage)reset?console.log('Timestamp expired, not decrementing usage.\n'+config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour.'):console.log(config.wordnik.date.remain+'/'+config.wordnik.date.limit+' requests remaining this hour, will reset in '+(59-minutes)+' minutes.');}else throw new Error('HTTP '+error.statusCode+': '+error.reponse.body);});}else throw new Error('Reached this hour\'s usage limit of '+config.wordnik.date.limit+'.');}; -------------------------------------------------------------------------------- /bin/leximaven.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict';/* eslint max-len: 0, no-unused-expressions: 0 */var chalk=require('chalk');var pkg=require('../package.json');var yargonaut=require('yargonaut').style('bold.underline','Commands:').style('bold.underline','Options:').style('bold.cyan','boolean').style('bold.yellow','string').style('bold.magenta','number').style('bold.blue','default:').style('bold.green','aliases:');var yargs=require('yargs');yargs.commandDir('commands').usage(chalk.yellow(''+yargonaut.asFont('leximaven','Small Slant'))+'\n'+chalk.bold.underline('Usage:')+'\n$0 [options]').help('h').alias('h','help').option('v',{alias:'verbose',type:'boolean',desc:'Verbose output'}).version('V','Show current version',pkg.version).alias('V','version').global('v').demand(1).argv; -------------------------------------------------------------------------------- /bin/themes.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len:0 */var _=require('lodash');var chalk=require('chalk');var fs=require('fs');var glob=require('glob');var noon=require('noon');var TDIR=null;var themeDirExists=null;try{fs.statSync('themes');themeDirExists=true;}catch(e){if(e.code==='ENOENT')themeDirExists=false;}themeDirExists?TDIR='themes/':TDIR=process.env.NODE_PATH+'/leximaven/themes/';/** 2 | * The themes module provides useful repetitive theme tasks 3 | * @module Themes 4 | *//** 5 | * Loads theme 6 | * @public 7 | * @param {string} theme The name of the theme 8 | * @return {Object} load The style to use 9 | */exports.loadTheme=function(theme){var dirExists=null;var load=null;try{fs.statSync('themes');dirExists=true;}catch(e){if(e.code==='ENOENT')dirExists=false;}var CFILE=process.env.HOME+'/.leximaven.noon';var config=noon.load(CFILE);if(!dirExists&&config.verbose)console.log(chalk.white(process.cwd()+'/themes does not exist, falling back to '+process.env.NODE_PATH+'/leximaven/themes.'));load=noon.load(''+TDIR+theme+'.noon');return load;};/** 10 | * Gets themes for list command 11 | * @public 12 | * @return {Array} List of theme names 13 | */exports.getThemes=function(){var list=[];var dirExists=null;var files=[];try{fs.statSync('themes');dirExists=true;}catch(e){if(e.code==='ENOENT')dirExists=false;}var CFILE=process.env.HOME+'/.leximaven.noon';var config=noon.load(CFILE);if(!dirExists&&config.verbose)console.log(chalk.white(process.cwd()+'/themes does not exist, falling back to '+process.env.NODE_PATH+'/leximaven/themes.'));files=glob.sync(TDIR+'*.noon');for(var i=0;i<=files.length-1;i++){list.push(files[i].replace(/[a-z0-9/_.]*themes\//,'').replace(/\.noon/,''));}return list;};/** 14 | * Prints label, connector, and content 15 | * @public 16 | * @param {Object} theme The style to use 17 | * @param {string} direction 'down' or 'right' 18 | * @param {string} text The label text 19 | * @param {string} [content] The text the label points at 20 | * @return {string} The stylized string to log 21 | */exports.label=function(theme,direction,text,content){var pstyle=_.get(chalk,theme.prefix.style);var tstyle=_.get(chalk,theme.text.style);var sstyle=_.get(chalk,theme.suffix.style);var cnstyle=_.get(chalk,theme.connector.style);var ctstyle=_.get(chalk,theme.content.style);var label=''+pstyle(theme.prefix.str)+tstyle(text)+sstyle(theme.suffix.str);if(direction==='right'){content!==null&&content!==undefined?label=''+label+cnstyle(theme.connector.str)+ctstyle(content):label=''+label;}else if(direction==='down'){content!==null&&content!==undefined?label=label+'\n'+cnstyle(theme.connector.str)+ctstyle(content):label=''+label;}else{throw new Error("Unsupported label direction, use 'down' or 'right'.");}console.log(label);return label;}; -------------------------------------------------------------------------------- /bin/tools.js: -------------------------------------------------------------------------------- 1 | 'use strict';/* eslint max-len: 0 */var chalk=require('chalk');var df=require('date-fns');var fs=require('fs-extra');var noon=require('noon');var ts=require('term-size');var wrap=require('wrap-ansi');var xml2js=require('xml2js');var CFILE=process.env.HOME+'/.leximaven.noon';/** 2 | * The tools module provides useful repetitive tasks 3 | * @module Utils 4 | *//** 5 | * Onelook's API limit check 6 | * @param {Object} config The current config 7 | * @return {Array} Updated config, proceed boolean, and reset boolean 8 | */exports.limitOnelook=function(config){var c=config;var proceed=false;var reset=false;var stamp=new Date(c.onelook.date.stamp);var hours=df.differenceInHours(new Date(),stamp);if(hours<24){c.onelook.date.remain--;}else if(hours>=24){reset=true;c.onelook.date.stamp=new Date().toJSON();c.onelook.date.remain=c.onelook.date.limit;c.onelook.date.remain--;}c.onelook.date.remain<=0?c.onelook.date.remain=0:proceed=true;noon.save(CFILE,c);return[c,proceed,reset];};/** 9 | * Datamuse's API limit check 10 | * @param {Object} config The current config 11 | * @return {Array} Updated config, proceed boolean, and reset boolean 12 | */exports.limitDmuse=function(config){var c=config;var proceed=false;var reset=false;var stamp=new Date(c.dmuse.date.stamp);var hours=df.differenceInHours(new Date(),stamp);if(hours<24){c.dmuse.date.remain--;}else if(hours>=24){reset=true;c.dmuse.date.stamp=new Date().toJSON();c.dmuse.date.remain=c.dmuse.date.limit;c.dmuse.date.remain--;}c.dmuse.date.remain<=0?c.dmuse.date.remain=0:proceed=true;noon.save(CFILE,c);return[c,proceed,reset];};/** 13 | * Rhymebrain's API limit check 14 | * @param {Object} config The current config 15 | * @return {Array} Updated config, proceed boolean, and reset boolean 16 | */exports.limitRbrain=function(config){var c=config;var proceed=false;var reset=false;var stamp=new Date(c.rbrain.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);if(minutes<60){c.rbrain.date.remain--;}else if(minutes>=60){reset=true;c.rbrain.date.stamp=new Date().toJSON();c.rbrain.date.remain=c.rbrain.date.limit;c.rbrain.date.remain--;}c.rbrain.date.remain<=0?c.rbrain.date.remain=0:proceed=true;noon.save(CFILE,c);return[c,proceed,reset];};/** 17 | * Wordnik's API limit check 18 | * @param {Object} config The current config 19 | * @return {Array} Updated config, proceed boolean, and reset boolean 20 | */exports.limitWordnik=function(config){var c=config;var proceed=false;var reset=false;var stamp=new Date(c.wordnik.date.stamp);var minutes=df.differenceInMinutes(new Date(),stamp);if(minutes<60){c.wordnik.date.remain--;}else if(minutes>=60){reset=true;c.wordnik.date.stamp=new Date().toJSON();c.wordnik.date.remain=c.wordnik.date.limit;c.wordnik.date.remain--;}c.wordnik.date.remain<=0?c.wordnik.date.remain=0:proceed=true;noon.save(CFILE,c);return[c,proceed,reset];};/** 21 | * Checks if a file exists 22 | * @private 23 | * @param {string} path The filename to check. 24 | * @return {boolean} fileExists 25 | */function checkOutfile(path){var fileExists=null;try{fs.statSync(path);fileExists=true;}catch(e){if(e.code==='ENOENT')fileExists=false;}return fileExists;}/** 26 | * Converts string to boolean 27 | * @public 28 | * @param {string} value 29 | * @return {boolean} v 30 | */exports.checkBoolean=function(value){var v=value;if(v==='true')v=true;if(v==='false')v=false;return v;};/** 31 | * Converts a boolean to a 0 or 1 32 | * @param {boolean} value A boolean value 33 | * @return {integer} 0 or 1 34 | */exports.boolToBin=function(value){var r=null;value?r=1:r=0;return r;};/** 35 | * Checks if config exists. If not, prints init message and exits with error code. 36 | * @public 37 | * @param {string} file Configuration filepath 38 | */exports.checkConfig=function(file){try{fs.statSync(file);}catch(e){if(e.code==='ENOENT')throw new Error('No config found at '+file+', run: \'leximaven config init\'');}return true;};/** 39 | * Checks if object is a single string in an array 40 | * @public 41 | * @param {Object} obj Any object 42 | * @return {Object} Original object or extracted string 43 | */exports.arrToStr=function(obj){var fixed=null;Array.isArray(obj)&&obj.length===1&&typeof obj[0]==='string'?fixed=obj[0]:fixed=obj;return fixed;};/** 44 | * Strips HTML from a string 45 | * @public 46 | * @param {string} string Text with HTML tags 47 | * @return {string} Plain text string 48 | */exports.stripHTML=function(string){return string.replace(/(<([^>]+)>)/ig,'');};/** 49 | * Wraps blocks of text 50 | * @param {string} str Long string 51 | * @param {boolean} hard true, soft false 52 | * @param {boolean} wwrap true, column wrap false 53 | * @return {string} ANSI-wrapped string 54 | */exports.wrapStr=function(str,hard,wwrap){var termsize=ts();return wrap(str,termsize.columns,hard,wwrap);};/** 55 | * Handles data export to file. Supports cson, json, noon, plist, xml, yaml. 56 | * @public 57 | * @param {string} path The desired filepath and extension 58 | * @param {boolean} force Whether to force overwrite 59 | * @param {Object} tofile A numbered object of data points 60 | */exports.outFile=function(path,force,tofile){var match=path.match(/\.([a-z]*)$/i);var ext=match[1];var builder=new xml2js.Builder();if(ext==='xml'){if(checkOutfile(path)){if(force){var xml=builder.buildObject(tofile);var fd=fs.openSync(path,'w+');fs.writeSync(fd,xml);fs.closeSync(fd);console.log(chalk.white('Overwrote '+path+' with data.'));}else console.log(chalk.white(path+' exists, use -f to force overwrite.'));}else{var _xml=builder.buildObject(tofile);var _fd=fs.openSync(path,'w+');fs.writeSync(_fd,_xml);fs.closeSync(_fd);console.log(chalk.white('Wrote data to '+path+'.'));}}else if(ext==='cson'||ext==='json'||ext==='noon'||ext==='plist'||ext==='yml'||ext==='yaml'){if(checkOutfile(path)){if(force){noon.save(path,tofile);console.log(chalk.white('Overwrote '+path+' with data.'));}else console.log(chalk.white(path+' exists, use -f to force overwrite.'));}else{noon.save(path,tofile);console.log(chalk.white('Wrote data to '+path+'.'));}}else if(ext!=='xml'||ext!=='cson'||ext!=='json'||ext!=='noon'||ext!=='plist'||ext!=='yml'||ext!=='yaml')throw new Error('Format '+ext+' not supported.');}; -------------------------------------------------------------------------------- /default.config.noon: -------------------------------------------------------------------------------- 1 | anagram 2 | case 1 3 | lang english 4 | limit 10 5 | linenum false 6 | list false 7 | maxletter 50 8 | maxword 10 9 | minletter 1 10 | repeat false 11 | dmuse 12 | date 13 | interval day 14 | limit 100000 15 | remain 100000 16 | stamp || 17 | max 5 18 | merge true 19 | onelook 20 | date 21 | interval day 22 | limit 10000 23 | remain 10000 24 | stamp || 25 | links false 26 | rbrain 27 | combine 28 | lang en 29 | max 5 30 | date 31 | interval hour 32 | limit 350 33 | remain 350 34 | stamp || 35 | info 36 | lang en 37 | rhyme 38 | lang en 39 | max 50 40 | theme square 41 | urban 42 | limit 5 43 | usage true 44 | verbose true 45 | wordmap 46 | limit 1 47 | wordnik 48 | date 49 | interval hour 50 | limit 15000 51 | remain 15000 52 | stamp || 53 | define 54 | canon false 55 | defdict all 56 | limit 5 57 | part || 58 | example 59 | canon false 60 | limit 5 61 | skip 0 62 | hyphen 63 | canon false 64 | dict all 65 | limit 5 66 | origin 67 | canon false 68 | phrase 69 | canon false 70 | limit 5 71 | weight 13 72 | pronounce 73 | canon false 74 | dict || 75 | limit 5 76 | type || 77 | relate 78 | canon false 79 | limit 10 80 | type || 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leximaven", 3 | "version": "4.0.0", 4 | "description": "A command line tool for searching word-related APIs.", 5 | "author": "Andrew Prentice", 6 | "license": "MIT", 7 | "config": { 8 | "commitizen": { 9 | "path": "./node_modules/cz-conventional-changelog" 10 | } 11 | }, 12 | "main": "bin/leximaven.js", 13 | "bin": { 14 | "leximaven": "bin/leximaven.js" 15 | }, 16 | "scripts": { 17 | "bin": "cross-env BABEL_ENV=dev babel src -d bin", 18 | "bump": "git push origin master --tags && npm publish", 19 | "instrument": "cross-env BABEL_ENV=test babel src -d bin", 20 | "lint": "standard --fix src", 21 | "release": "standard-version", 22 | "test": "echo 'No tests.'", 23 | "watch": "babel -w src -d bin" 24 | }, 25 | "dependencies": { 26 | "acorn": "*", 27 | "ansi-colors": "*", 28 | "cz-conventional-changelog": "*", 29 | "date-fns": "*", 30 | "dot-prop": "*", 31 | "fs-extra": "*", 32 | "glob": "*", 33 | "good-guy-http": "*", 34 | "lodash": "*", 35 | "noon": "*", 36 | "ora": "*", 37 | "random-word": "*", 38 | "scrape-it": "*", 39 | "term-size": "*", 40 | "wrap-ansi": "*", 41 | "xml2js": "*", 42 | "yargonaut": "*", 43 | "yargs": "*" 44 | }, 45 | "devDependencies": { 46 | "babel-core": "*", 47 | "babel-plugin-lodash": "*", 48 | "babel-preset-env": "*", 49 | "babel-register": "*", 50 | "cross-env": "*", 51 | "has-ansi": "*", 52 | "rimraf": "*", 53 | "standard": "*" 54 | }, 55 | "repository": { 56 | "type": "git", 57 | "url": "git+https://github.com/drawnepicenter/leximaven.git" 58 | }, 59 | "bugs": { 60 | "url": "https://github.com/drawnepicenter/leximaven/issues" 61 | }, 62 | "homepage": "https://github.com/drawnepicenter/leximaven#readme", 63 | "keywords": [ 64 | "lyracyst", 65 | "lyricist", 66 | "leximaven", 67 | "cli", 68 | "word", 69 | "search", 70 | "api", 71 | "scraper", 72 | "rest", 73 | "anagram", 74 | "acronym", 75 | "define", 76 | "dictionary", 77 | "thesaurus", 78 | "slang", 79 | "rhyme", 80 | "pronunciation" 81 | ], 82 | "packageManager": "pnpm@8.10.2+sha1.e0b68270e89c817ff88b7be62466a2128c53af02" 83 | } 84 | -------------------------------------------------------------------------------- /src/commands/acronym.js: -------------------------------------------------------------------------------- 1 | const themes = require('../themes') 2 | const tools = require('../tools') 3 | 4 | const _ = require('lodash') 5 | const chalk = require('chalk') 6 | const http = require('good-guy-http')() 7 | const noon = require('noon') 8 | const xml2js = require('xml2js') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | exports.command = 'acronym ' 13 | exports.aliases = ['acro', 'ac'] 14 | exports.desc = 'Acronyms' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | } 28 | } 29 | exports.handler = (argv) => { 30 | tools.checkConfig(CFILE) 31 | const config = noon.load(CFILE) 32 | const theme = themes.loadTheme(config.theme) 33 | if (config.verbose) themes.label(theme, 'down', 'Acronyms') 34 | const acronym = argv.acronym.toUpperCase() 35 | const url = `http://acronyms.silmaril.ie/cgi-bin/xaa?${argv.acronym}` 36 | const tofile = { 37 | type: 'acronym', 38 | source: 'http://acronyms.silmaril.ie', 39 | url 40 | } 41 | const ctstyle = _.get(chalk, theme.content.style) 42 | http({ url }, (error, response) => { 43 | if (!error && response.statusCode === 200) { 44 | const body = response.body 45 | const parser = new xml2js.Parser() 46 | parser.parseString(body, (err, result) => { 47 | if (!err) { 48 | const found = result.acronym.found[0] 49 | const count = found.$ 50 | if (count.n === '0') { 51 | console.log(ctstyle(`Found 0 acronyms for ${acronym}.`)) 52 | } else { 53 | console.log(ctstyle(`Found ${count.n} acronyms for ${acronym}:`)) 54 | const list = found.acro 55 | for (let i = 0; i <= list.length - 1; i++) { 56 | const item = list[i] 57 | process.stdout.write(ctstyle(`${item.expan}`)) 58 | tofile[[`expansion${i}`]] = item.expan[0] 59 | const comm = item.comment[0] 60 | if (comm !== '') { 61 | if (comm.a) { 62 | const comment = comm.a[0] 63 | process.stdout.write(ctstyle(` - ${comment._} - ${comment.$.href}`)) 64 | tofile[[`comment${i}`]] = comment._ 65 | tofile[[`url${i}`]] = comment.$.href 66 | } else { 67 | process.stdout.write(ctstyle(` - ${comm}`)) 68 | tofile[[`comment${i}`]] = item.comment[0] 69 | } 70 | } 71 | console.log(ctstyle(` - DDC: ${item.$.dewey}`)) 72 | tofile[[`DDC${i}`]] = item.$.dewey 73 | } 74 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 75 | } 76 | } else { 77 | throw new Error(err) 78 | } 79 | }) 80 | } else { 81 | throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 82 | } 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /src/commands/completion.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars:0, no-unused-expressions:0 */ 2 | const yargs = require('yargs') 3 | exports.command = 'completion' 4 | exports.aliases = ['comp'] 5 | exports.desc = 'Print shell completion script' 6 | exports.builder = {} 7 | exports.handler = (argv) => { 8 | yargs.showCompletionScript().argv 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/configuration.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | exports.command = 'configuration ' 3 | exports.aliases = ['conf', 'config'] 4 | exports.desc = 'Configuration tasks' 5 | exports.builder = (yargs) => yargs.commandDir('configuration') 6 | exports.handler = (argv) => {} 7 | -------------------------------------------------------------------------------- /src/commands/configuration/get.js: -------------------------------------------------------------------------------- 1 | const themes = require('../../themes') 2 | const tools = require('../../tools') 3 | 4 | const chalk = require('chalk') 5 | const dot = require('dot-prop') 6 | const noon = require('noon') 7 | 8 | const CFILE = `${process.env.HOME}/.leximaven.noon` 9 | 10 | exports.command = 'get ' 11 | exports.aliases = ['g'] 12 | exports.desc = 'Retrieve a config value' 13 | exports.builder = {} 14 | exports.handler = (argv) => { 15 | const key = argv.key 16 | let value = null 17 | tools.checkConfig(CFILE) 18 | const config = noon.load(CFILE) 19 | const theme = themes.loadTheme(config.theme) 20 | if (config.verbose) themes.label(theme, 'down', 'Configuration') 21 | if (dot.has(config, key)) { 22 | value = /\./i.test(key) ? dot.get(config, key) : config[key] 23 | } else { 24 | throw new Error(`Option ${key} not found.`) 25 | } 26 | console.log(`Option ${chalk.white.bold(key)} is ${chalk.white.bold(value)}.`) 27 | } 28 | -------------------------------------------------------------------------------- /src/commands/configuration/init.js: -------------------------------------------------------------------------------- 1 | const themes = require('../../themes') 2 | 3 | const chalk = require('chalk') 4 | const fs = require('fs') 5 | const noon = require('noon') 6 | const os = require('os') 7 | 8 | const CFILE = `${process.env.HOME}/.leximaven.noon` 9 | const PKGDIR = `${process.env.NODE_PATH}/leximaven/` 10 | 11 | exports.command = 'init' 12 | exports.aliases = ['i'] 13 | exports.desc = 'Initialize config file' 14 | exports.builder = { 15 | force: { 16 | alias: 'f', 17 | desc: 'Force overwriting configuration file', 18 | default: false, 19 | type: 'boolean' 20 | } 21 | } 22 | exports.handler = (argv) => { 23 | let obj = null 24 | let configExists = null 25 | let dirExists = null 26 | try { 27 | fs.statSync('default.config.noon') 28 | configExists = true 29 | } catch (e) { 30 | if (e.code === 'ENOENT') configExists = false 31 | } 32 | if (configExists) { 33 | obj = noon.load('default.config.noon') 34 | } else { 35 | try { 36 | fs.statSync(PKGDIR) 37 | dirExists = true 38 | } catch (e) { 39 | if (e.code === 'ENOENT') { 40 | dirExists = false 41 | } 42 | } 43 | if (dirExists) { 44 | obj = noon.load(`${PKGDIR}default.config.noon`) 45 | } else { 46 | throw new Error('Package dir not found, set NODE_PATH per documentation.') 47 | } 48 | } 49 | obj.dmuse.date.stamp = new Date().toJSON() 50 | obj.onelook.date.stamp = new Date().toJSON() 51 | obj.rbrain.date.stamp = new Date().toJSON() 52 | obj.wordnik.date.stamp = new Date().toJSON() 53 | let fileExists = null 54 | try { 55 | fs.statSync(CFILE) 56 | fileExists = true 57 | } catch (e) { 58 | if (e.code === 'ENOENT') { 59 | fileExists = false 60 | } 61 | } 62 | if (fileExists) { 63 | if (argv.f) { 64 | const config = noon.load(CFILE) 65 | obj.dmuse.date.stamp = config.dmuse.date.stamp 66 | obj.dmuse.date.remain = config.dmuse.date.remain 67 | obj.onelook.date.stamp = config.onelook.date.stamp 68 | obj.onelook.date.remain = config.onelook.date.remain 69 | obj.rbrain.date.stamp = config.rbrain.date.stamp 70 | obj.rbrain.date.remain = config.rbrain.date.remain 71 | obj.wordnik.date.stamp = config.wordnik.date.stamp 72 | obj.wordnik.date.remain = config.wordnik.date.remain 73 | noon.save(CFILE, obj) 74 | console.log(`Overwrote ${chalk.white.bold(CFILE)}.`) 75 | } else { 76 | console.log(`Using configuration at ${chalk.white.bold(CFILE)}.`) 77 | } 78 | } else if (!fileExists) { 79 | noon.save(CFILE, obj) 80 | console.log(`Created ${chalk.white.bold(CFILE)}.`) 81 | } 82 | const config = noon.load(CFILE) 83 | const theme = themes.loadTheme(config.theme) 84 | if (os.platform() === 'windows') themes.label(theme, 'right', 'Notice', 'Please see the README for best user experience on Windows.') 85 | if (argv.v) { 86 | themes.label(theme, 'down', 'Configuration') 87 | console.log('Your current configuration is:') 88 | console.log(noon.stringify(config, { 89 | indent: 2, 90 | align: true, 91 | maxalign: 32, 92 | sort: true, 93 | colors: true 94 | })) 95 | console.log('') 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/commands/configuration/set.js: -------------------------------------------------------------------------------- 1 | const themes = require('../../themes') 2 | const tools = require('../../tools') 3 | 4 | const chalk = require('chalk') 5 | const dot = require('dot-prop') 6 | const noon = require('noon') 7 | 8 | const CFILE = `${process.env.HOME}/.leximaven.noon` 9 | 10 | exports.command = 'set ' 11 | exports.aliases = ['s'] 12 | exports.desc = 'Set a config value' 13 | exports.builder = {} 14 | exports.handler = (argv) => { 15 | const key = argv.key 16 | let value = argv.value 17 | value = tools.checkBoolean(value) 18 | tools.checkConfig(CFILE) 19 | const config = noon.load(CFILE) 20 | const theme = themes.loadTheme(config.theme) 21 | if (config.verbose) themes.label(theme, 'down', 'Configuration') 22 | if (dot.has(config, key)) { 23 | if (/\./i.test(key)) { 24 | if (/^\w*\.date/i.test(key)) { 25 | throw new Error("API limits hardcoded, can't set this key.") 26 | } else { 27 | dot.set(config, key, value) 28 | } 29 | } else { 30 | config[key] = value 31 | } 32 | } else { 33 | throw new Error(`Option ${key} not found.`) 34 | } 35 | noon.save(CFILE, config) 36 | console.log(`Set option ${chalk.white.bold(key)} to ${chalk.white.bold(value)}.`) 37 | } 38 | -------------------------------------------------------------------------------- /src/commands/datamuse.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | exports.command = 'datamuse ' 3 | exports.aliases = ['dmuse', 'dm'] 4 | exports.desc = 'Datamuse tasks' 5 | exports.builder = (yargs) => yargs.commandDir('datamuse') 6 | exports.handler = (argv) => {} 7 | -------------------------------------------------------------------------------- /src/commands/datamuse/get.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const chalk = require('chalk') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | const ora = require('ora') 11 | 12 | const CFILE = `${process.env.HOME}/.leximaven.noon` 13 | 14 | exports.command = 'get ' 15 | exports.desc = 'Datamuse query' 16 | exports.builder = { 17 | out: { 18 | alias: 'o', 19 | desc: 'Write cson, json, noon, plist, yaml, xml', 20 | default: '', 21 | type: 'string' 22 | }, 23 | force: { 24 | alias: 'f', 25 | desc: 'Force overwriting outfile', 26 | default: false, 27 | type: 'boolean' 28 | }, 29 | save: { 30 | alias: 's', 31 | desc: 'Save flags to config file', 32 | default: false, 33 | type: 'boolean' 34 | }, 35 | max: { 36 | alias: 'm', 37 | desc: 'Maximum number of results, 1 to 1000', 38 | default: 5, 39 | type: 'number' 40 | } 41 | } 42 | exports.handler = (argv) => { 43 | tools.checkConfig(CFILE) 44 | let config = noon.load(CFILE) 45 | let proceed = false 46 | let reset = false 47 | const stamp = new Date(config.dmuse.date.stamp) 48 | const hours = df.differenceInHours(new Date(), stamp) 49 | const minutes = df.differenceInMinutes(new Date(), stamp) 50 | const checkStamp = tools.limitDmuse(config) 51 | config = checkStamp[0] 52 | proceed = checkStamp[1] 53 | reset = checkStamp[2] 54 | if (proceed) { 55 | const userConfig = { 56 | dmuse: { 57 | max: argv.m 58 | } 59 | } 60 | if (config.merge) config = _.merge({}, config, userConfig) 61 | if (argv.s && config.merge) noon.save(CFILE, config) 62 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 63 | const theme = themes.loadTheme(config.theme) 64 | if (config.verbose) themes.label(theme, 'down', 'Datamuse') 65 | const ccont = [] 66 | ccont.push(argv.condition) 67 | if (argv._.length > 1) { 68 | for (let i = 0; i <= argv._.length - 1; i++) { 69 | if (argv._[i] !== 'datamuse' && argv._[i] !== 'dmuse' && argv._[i] !== 'dm' && argv._[i] !== 'get') { 70 | ccont.push(argv._[i]) 71 | } 72 | } 73 | } 74 | const prefix = 'http://api.datamuse.com/words?' 75 | let conditions = `max=${config.dmuse.max}&` 76 | for (let i = 0; i <= ccont.length - 1; i++) { conditions = `${conditions}&${ccont[i]}` } 77 | let url = `${prefix}${conditions}` 78 | url = encodeURI(url) 79 | const tags = { 80 | n: 'noun', 81 | adj: 'adjective', 82 | adv: 'adverb', 83 | syn: 'synonym' 84 | } 85 | const tofile = { 86 | type: 'datamuse', 87 | source: 'http://datamuse.com/api', 88 | url 89 | } 90 | const ctstyle = _.get(chalk, theme.content.style) 91 | const spinner = ora({ 92 | text: `${chalk.bold.cyan('Loading results...')}`, 93 | spinner: 'dots8', 94 | color: 'yellow' 95 | }) 96 | http({ url }, (error, response) => { 97 | if (!error && response.statusCode === 200) { 98 | if (response.headers['x-gg-state'] === 'cached') { 99 | config.dmuse.date.remain++ 100 | noon.save(CFILE, config) 101 | if (config.usage) console.log('Cached response, not decrementing usage.') 102 | } 103 | const resp = JSON.parse(response.body) 104 | spinner.stop() 105 | spinner.clear() 106 | for (let i = 0; i <= resp.length - 1; i++) { 107 | const item = resp[i] 108 | themes.label(theme, 'right', 'Match', `${item.word} `) 109 | tofile[[`match${i}`]] = item.word 110 | if (item.tags !== undefined && item.tags !== []) { 111 | themes.label(theme, 'right', 'Tag') 112 | for (let j = 0; j <= item.tags.length - 1; j++) { 113 | if (j === item.tags.length - 1) { 114 | process.stdout.write(ctstyle(`${tags[item.tags[j]]}`)) 115 | tofile[[`tags${j}`]] = tags[item.tags[j]] 116 | } else process.stdout.write(ctstyle(`${tags[item.tags[j]]}, `)) 117 | } 118 | console.log('') 119 | } 120 | } 121 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 122 | if (config.usage) reset ? console.log(`Timestamp expired, reset usage limits.\n${config.dmuse.date.remain}/${config.dmuse.date.limit} requests remaining today.`) : console.log(`${config.dmuse.date.remain}/${config.dmuse.date.limit} requests remaining today, will reset in ${23 - hours} hours, ${59 - minutes} minutes.`) 123 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 124 | }) 125 | } else throw new Error(`Reached today's usage limit of ${config.dmuse.date.limit}.`) 126 | } 127 | -------------------------------------------------------------------------------- /src/commands/datamuse/info.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0, no-unused-vars:0 */ 2 | const tools = require('../../tools') 3 | 4 | const chalk = require('chalk') 5 | const df = require('date-fns') 6 | const http = require('good-guy-http')() 7 | const noon = require('noon') 8 | 9 | const CFILE = `${process.env.HOME}/.leximaven.noon` 10 | 11 | exports.command = 'info' 12 | exports.desc = 'Datamuse metrics' 13 | exports.builder = {} 14 | exports.handler = (argv) => { 15 | tools.checkConfig(CFILE) 16 | const config = noon.load(CFILE) 17 | const url = 'http://api.datamuse.com/metrics' 18 | http({ url }, (error, response) => { 19 | if (!error && response.statusCode === 200) { 20 | const body = JSON.parse(response.body) 21 | const version = body[0] 22 | const qps = body[1] 23 | const sugf = body[2] 24 | const sugn = body[3] 25 | const wordf = body[4] 26 | const wordn = body[5] 27 | console.log(chalk.white(`Current queries per second (v${Math.round(version.value * 100) / 100.0}): ${Math.round(qps.value * 100) / 100.0}`)) 28 | console.log(chalk.white(`Latency (/words): ${Math.round(wordf.value * 100000) / 100.0} ms (median), ${Math.round(wordn.value * 100000) / 100.0} ms (99 %ile)`)) 29 | console.log(chalk.white(`Latency (/sug): ${Math.round(sugf.value * 100000) / 100.0} ms (median), ${Math.round(sugn.value * 100000) / 100.0} ms (99 %ile)`)) 30 | } else { 31 | throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 32 | } 33 | }) 34 | const limit = config.dmuse.date.limit 35 | const remain = config.dmuse.date.remain 36 | const stamp = new Date(config.dmuse.date.stamp) 37 | const hours = df.differenceInHours(new Date(), stamp) 38 | const minutes = df.differenceInMinutes(new Date(), stamp) 39 | console.log(chalk.white(`${remain}/${limit} requests remain today, will reset in ${23 - hours} hours, ${59 - minutes} minutes.`)) 40 | } 41 | -------------------------------------------------------------------------------- /src/commands/list.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | const themes = require('../themes') 3 | 4 | exports.command = 'list' 5 | exports.aliases = ['ls', 'themes'] 6 | exports.desc = 'Get a list of installed themes' 7 | exports.builder = {} 8 | exports.handler = (argv) => { 9 | const list = themes.getThemes() 10 | for (let i = 0; i <= list.length - 1; i++) { 11 | const currentTheme = themes.loadTheme(list[i]) 12 | const sample = 'Morbi ornare pulvinar metus, non faucibus arcu ultricies non.' 13 | themes.label(currentTheme, 'down', list[i], sample) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/commands/onelook.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../themes') 3 | const tools = require('../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const chalk = require('chalk') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | const xml2js = require('xml2js') 11 | 12 | const CFILE = `${process.env.HOME}/.leximaven.noon` 13 | 14 | exports.command = 'onelook ' 15 | exports.aliases = ['one', 'ol'] 16 | exports.desc = 'Onelook definitions' 17 | exports.builder = { 18 | out: { 19 | alias: 'o', 20 | desc: 'Write cson, json, noon, plist, yaml, xml', 21 | default: '', 22 | type: 'string' 23 | }, 24 | force: { 25 | alias: 'f', 26 | desc: 'Force overwriting outfile', 27 | default: false, 28 | type: 'boolean' 29 | }, 30 | save: { 31 | alias: 's', 32 | desc: 'Save flags to config file', 33 | default: false, 34 | type: 'boolean' 35 | }, 36 | links: { 37 | alias: 'l', 38 | desc: 'Include resource links', 39 | default: false, 40 | type: 'boolean' 41 | } 42 | } 43 | exports.handler = (argv) => { 44 | tools.checkConfig(CFILE) 45 | let config = noon.load(CFILE) 46 | let proceed = false 47 | let reset = false 48 | const checkStamp = tools.limitOnelook(config) 49 | config = checkStamp[0] 50 | proceed = checkStamp[1] 51 | reset = checkStamp[2] 52 | const stamp = new Date(config.onelook.date.stamp) 53 | const hours = df.differenceInHours(new Date(), stamp) 54 | const minutes = df.differenceInMinutes(new Date(), stamp) 55 | if (proceed) { 56 | const userConfig = { 57 | onelook: { 58 | links: argv.l 59 | } 60 | } 61 | if (config.merge) config = _.merge({}, config, userConfig) 62 | if (argv.s && config.merge) noon.save(CFILE, config) 63 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 64 | const theme = themes.loadTheme(config.theme) 65 | if (config.verbose) themes.label(theme, 'down', 'Onelook') 66 | const acont = [] 67 | acont.push(argv.word) 68 | if (argv._.length > 1) { 69 | for (let i = 0; i <= argv._.length - 1; i++) { 70 | if (argv._[i] !== 'onelook' && argv._[i] !== 'one' && argv._[i] !== 'ol') acont.push(argv._[i]) 71 | } 72 | } 73 | let url = `http://onelook.com/?xml=1&w=${acont.join('+')}` 74 | url = encodeURI(url) 75 | const tofile = { 76 | type: 'onelook', 77 | source: 'http://www.onelook.com', 78 | url 79 | } 80 | const ctstyle = _.get(chalk, theme.content.style) 81 | http({ url }, (error, response) => { 82 | if (!error && response.statusCode === 200) { 83 | if (response.headers['x-gg-state'] === 'cached') { 84 | config.onelook.date.remain++ 85 | noon.save(CFILE, config) 86 | if (config.usage) console.log('Cached response, not decrementing usage.') 87 | } 88 | const body = response.body 89 | const parser = new xml2js.Parser() 90 | parser.parseString(body, (err, result) => { 91 | if (!err) { 92 | const resp = result.OLResponse 93 | const phrase = resp.OLPhrases[0] 94 | const similar = resp.OLSimilar[0] 95 | const quickdef = resp.OLQuickDef 96 | const resources = resp.OLRes 97 | themes.label(theme, 'down', 'Definition') 98 | if (Array.isArray(quickdef) && quickdef.length > 1) { 99 | for (let i = 0; i <= quickdef.length - 1; i++) { 100 | let item = quickdef[i] 101 | item = item.replace(/<|>|\n|\/i/g, '') 102 | item = item.replace(/i"/g, '"') 103 | console.log(ctstyle(item)) 104 | tofile[[`definition${i}`]] = item 105 | } 106 | } else { 107 | const definition = quickdef[0].replace(/<|>|\n|\/i/g, '') 108 | console.log(ctstyle(definition)) 109 | tofile.definition = definition 110 | } 111 | if (phrase) { 112 | const phrases = phrase.replace(/\n/g, '') 113 | themes.label(theme, 'down', 'Phrases', phrases) 114 | tofile.phrase = phrases 115 | } 116 | if (similar) { 117 | const sim = similar.replace(/\n/g, '') 118 | themes.label(theme, 'down', 'Similar', sim) 119 | tofile.sim = sim 120 | } 121 | if (config.onelook.links) { 122 | themes.label(theme, 'down', 'Resources') 123 | for (let i = 0; i <= resources.length - 1; i++) { 124 | const item = resources[i] 125 | const res = tools.arrToStr(item.OLResName).replace(/\n/g, '') 126 | const link = tools.arrToStr(item.OLResLink).replace(/\n/g, '') 127 | const home = tools.arrToStr(item.OLResHomeLink).replace(/\n/g, '') 128 | themes.label(theme, 'right', res, link) 129 | tofile[[`res${i}`]] = res 130 | tofile[[`link${i}`]] = link 131 | tofile[[`home${i}`]] = home 132 | } 133 | } 134 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 135 | if (config.usage) { 136 | if (reset) { 137 | console.log('Timestamp expired, reset usage limits.') 138 | console.log(`${config.onelook.date.remain}/${config.onelook.date.limit} requests remaining today.`) 139 | } else console.log(`${config.onelook.date.remain}/${config.onelook.date.limit} requests remaining today, will reset in ${23 - hours} hours, ${59 - minutes} minutes.`) 140 | } 141 | } else { 142 | throw new Error(err) 143 | } 144 | }) 145 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 146 | }) 147 | } else throw new Error(`Reached today's usage limit of ${config.onelook.date.limit}.`) 148 | } 149 | -------------------------------------------------------------------------------- /src/commands/random.js: -------------------------------------------------------------------------------- 1 | const rand = require('random-word') 2 | 3 | exports.command = 'random' 4 | exports.aliases = ['rand', 'rw'] 5 | exports.desc = 'Get a random word' 6 | exports.builder = {} 7 | exports.handler = (argv) => { 8 | console.log(rand()) 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/rhymebrain.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | exports.command = 'rhymebrain ' 3 | exports.aliases = ['rbrain', 'rb'] 4 | exports.desc = 'Rhymebrain operations' 5 | exports.builder = (yargs) => yargs.commandDir('rhymebrain') 6 | exports.handler = (argv) => {} 7 | -------------------------------------------------------------------------------- /src/commands/rhymebrain/combine.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const http = require('good-guy-http')() 8 | const noon = require('noon') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | exports.command = 'combine ' 13 | exports.aliases = ['comb', 'portmanteau'] 14 | exports.desc = 'Rhymebrain portmanteaus' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | }, 28 | save: { 29 | alias: 's', 30 | desc: 'Save flags to config file', 31 | default: false, 32 | type: 'boolean' 33 | }, 34 | lang: { 35 | alias: 'l', 36 | desc: 'ISO 639-1 language code', 37 | default: 'en', 38 | type: 'string' 39 | }, 40 | max: { 41 | alias: 'm', 42 | desc: 'Max results to return', 43 | default: 5, 44 | type: 'number' 45 | } 46 | } 47 | exports.handler = (argv) => { 48 | tools.checkConfig(CFILE) 49 | let config = noon.load(CFILE) 50 | let proceed = false 51 | let reset = false 52 | const stamp = new Date(config.rbrain.date.stamp) 53 | const minutes = df.differenceInMinutes(new Date(), stamp) 54 | const checkStamp = tools.limitRbrain(config) 55 | config = checkStamp[0] 56 | proceed = checkStamp[1] 57 | reset = checkStamp[2] 58 | if (proceed) { 59 | const userConfig = { 60 | rbrain: { 61 | combine: { 62 | lang: argv.l, 63 | max: argv.m 64 | } 65 | } 66 | } 67 | if (config.merge) config = _.merge({}, config, userConfig) 68 | if (argv.s && config.merge) noon.save(CFILE, config) 69 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 70 | const theme = themes.loadTheme(config.theme) 71 | if (config.verbose) themes.label(theme, 'down', 'Rhymebrain') 72 | const query = argv.query 73 | const task = 'Portmanteaus' 74 | const prefix = 'http://rhymebrain.com/talk?function=get' 75 | const uri = `${prefix}${task}&word=${query}&` 76 | const pcont = [] 77 | pcont.push(`lang=${config.rbrain.combine.lang}&`) 78 | pcont.push(`maxResults=${config.rbrain.combine.max}&`) 79 | const rest = pcont.join('') 80 | let url = `${uri}${rest}` 81 | url = encodeURI(url) 82 | themes.label(theme, 'down', task) 83 | const tofile = { 84 | type: 'portmanteau', 85 | source: 'http://rhymebrain.com', 86 | url 87 | } 88 | http({ url }, (error, response) => { 89 | if (!error && response.statusCode === 200) { 90 | if (response.headers['x-gg-state'] === 'cached') { 91 | config.rbrain.date.remain++ 92 | noon.save(CFILE, config) 93 | if (config.usage) console.log('Cached response, not decrementing usage.') 94 | } 95 | const list = JSON.parse(response.body) 96 | for (let i = 0; i <= list.length - 1; i++) { 97 | const item = list[i] 98 | themes.label(theme, 'right', item.source, item.combined) 99 | tofile[[`set${i}`]] = item.source 100 | tofile[[`portmanteau${i}`]] = item.combined 101 | } 102 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 103 | if (config.usage) reset ? console.log(`Timestamp expired, reset usage limits.\n${config.rbrain.date.remain}/${config.rbrain.date.limit} requests remaining this hour.`) : console.log(`${config.rbrain.date.remain}/${config.rbrain.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 104 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 105 | }) 106 | } else throw new Error(`Reached this hour's usage limit of ${config.rbrain.date.limit}.`) 107 | } 108 | -------------------------------------------------------------------------------- /src/commands/rhymebrain/info.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const chalk = require('chalk') 7 | const df = require('date-fns') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | 11 | const CFILE = `${process.env.HOME}/.leximaven.noon` 12 | 13 | exports.command = 'info ' 14 | exports.desc = 'Rhymebrain word info' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | }, 28 | save: { 29 | alias: 's', 30 | desc: 'Save flags to config file', 31 | default: false, 32 | type: 'boolean' 33 | }, 34 | lang: { 35 | alias: 'l', 36 | desc: 'ISO 639-1 language code', 37 | default: 'en', 38 | type: 'string' 39 | } 40 | } 41 | exports.handler = (argv) => { 42 | tools.checkConfig(CFILE) 43 | let config = noon.load(CFILE) 44 | let proceed = false 45 | let reset = false 46 | const stamp = new Date(config.rbrain.date.stamp) 47 | const minutes = df.differenceInMinutes(new Date(), stamp) 48 | const checkStamp = tools.limitRbrain(config) 49 | config = checkStamp[0] 50 | proceed = checkStamp[1] 51 | reset = checkStamp[2] 52 | if (proceed) { 53 | const userConfig = { 54 | rbrain: { 55 | info: { 56 | lang: argv.l 57 | } 58 | } 59 | } 60 | if (config.merge) config = _.merge({}, config, userConfig) 61 | if (argv.s && config.merge) noon.save(CFILE, config) 62 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 63 | const theme = themes.loadTheme(config.theme) 64 | if (config.verbose) themes.label(theme, 'down', 'Rhymebrain') 65 | const word = argv.word 66 | const task = 'WordInfo' 67 | const prefix = 'http://rhymebrain.com/talk?function=get' 68 | const uri = `${prefix}${task}&word=${word}&lang=${config.rbrain.info.lang}` 69 | const url = encodeURI(uri) 70 | themes.label(theme, 'down', 'Word Info') 71 | const tofile = { 72 | type: 'word info', 73 | source: 'http://rhymebrain.com', 74 | url 75 | } 76 | const ctstyle = _.get(chalk, theme.content.style) 77 | http({ url }, (error, response) => { 78 | if (!error && response.statusCode === 200) { 79 | if (response.headers['x-gg-state'] === 'cached') { 80 | config.rbrain.date.remain++ 81 | noon.save(CFILE, config) 82 | if (config.usage) console.log('Cached response, not decrementing usage.') 83 | } 84 | const info = JSON.parse(response.body) 85 | themes.label(theme, 'right', 'Arpabet', info.pron) 86 | themes.label(theme, 'right', 'IPA', info.ipa) 87 | themes.label(theme, 'right', 'Syllables', info.syllables) 88 | tofile.arpabet = info.pron 89 | tofile.ipa = info.ipa 90 | tofile.syllables = info.syllables 91 | const flags = [] 92 | if (info.flags.match(/a/)) { 93 | flags.push(ctstyle(`[${chalk.red.bold('Offensive')}]`)) 94 | tofile.offensive = true 95 | } 96 | if (info.flags.match(/b/)) { 97 | flags.push(ctstyle('[Found in dictionary]')) 98 | tofile.dict = true 99 | } 100 | if (info.flags.match(/c/)) { 101 | flags.push(ctstyle('[Trusted pronunciation, not generated]')) 102 | tofile.trusted = true 103 | } 104 | themes.label(theme, 'right', 'Word Flags', flags.join('')) 105 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 106 | reset ? console.log(`Timestamp expired, reset usage limits.\n${config.rbrain.date.remain}/${config.rbrain.date.limit} requests remaining this hour.`) : console.log(`${config.rbrain.date.remain}/${config.rbrain.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 107 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 108 | }) 109 | } else throw new Error(`Reached this hour's usage limit of ${config.rbrain.date.limit}.`) 110 | } 111 | -------------------------------------------------------------------------------- /src/commands/rhymebrain/rhyme.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const chalk = require('chalk') 7 | const df = require('date-fns') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | 11 | const CFILE = `${process.env.HOME}/.leximaven.noon` 12 | 13 | exports.command = 'rhyme ' 14 | exports.aliases = ['rh'] 15 | exports.desc = 'Rhymebrain rhymes' 16 | exports.builder = { 17 | out: { 18 | alias: 'o', 19 | desc: 'Write cson, json, noon, plist, yaml, xml', 20 | default: '', 21 | type: 'string' 22 | }, 23 | force: { 24 | alias: 'f', 25 | desc: 'Force overwriting outfile', 26 | default: false, 27 | type: 'boolean' 28 | }, 29 | save: { 30 | alias: 's', 31 | desc: 'Save flags to config file', 32 | default: false, 33 | type: 'boolean' 34 | }, 35 | lang: { 36 | alias: 'l', 37 | desc: 'ISO 639-1 language code', 38 | default: 'en', 39 | type: 'string' 40 | }, 41 | max: { 42 | alias: 'm', 43 | desc: 'Max results to return', 44 | default: 5, 45 | type: 'number' 46 | } 47 | } 48 | exports.handler = (argv) => { 49 | tools.checkConfig(CFILE) 50 | let config = noon.load(CFILE) 51 | let proceed = false 52 | let reset = false 53 | const stamp = new Date(config.rbrain.date.stamp) 54 | const minutes = df.differenceInMinutes(new Date(), stamp) 55 | const checkStamp = tools.limitRbrain(config) 56 | config = checkStamp[0] 57 | proceed = checkStamp[1] 58 | reset = checkStamp[2] 59 | if (proceed) { 60 | const userConfig = { 61 | rbrain: { 62 | rhyme: { 63 | lang: argv.l, 64 | max: argv.m 65 | } 66 | } 67 | } 68 | if (config.merge) config = _.merge({}, config, userConfig) 69 | if (argv.s && config.merge) noon.save(CFILE, config) 70 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 71 | const theme = themes.loadTheme(config.theme) 72 | if (config.verbose) themes.label(theme, 'down', 'Rhymebrain') 73 | const word = argv.word 74 | const task = 'Rhymes' 75 | const prefix = 'http://rhymebrain.com/talk?function=get' 76 | const uri = `${prefix}${task}&word=${word}&` 77 | const pcont = [] 78 | pcont.push(`lang=${config.rbrain.rhyme.lang}&`) 79 | pcont.push(`maxResults=${config.rbrain.rhyme.max}&`) 80 | const rest = pcont.join('') 81 | let url = `${uri}${rest}` 82 | url = encodeURI(url) 83 | const tofile = { 84 | type: 'rhyme', 85 | source: 'http://rhymebrain.com', 86 | url 87 | } 88 | const ctstyle = _.get(chalk, theme.content.style) 89 | http({ url }, (error, response) => { 90 | if (!error && response.statusCode === 200) { 91 | if (response.headers['x-gg-state'] === 'cached') { 92 | config.rbrain.date.remain++ 93 | noon.save(CFILE, config) 94 | if (config.usage) console.log('Cached response, not decrementing usage.') 95 | } 96 | const list = JSON.parse(response.body) 97 | const lcont = [] 98 | for (let i = 0; i <= list.length - 1; i++) { 99 | lcont.push(list[i].word) 100 | } 101 | lcont.sort((a, b) => { 102 | if (a < b) return -1 103 | if (a > b) return 1 104 | return 0 105 | }) 106 | const rcont = [] 107 | for (let j = 0; j <= lcont.length - 1; j++) { 108 | const item = lcont[j] 109 | rcont.push(ctstyle(item)) 110 | item.score >= 300 ? tofile[[`hiscore${j}`]] = item : tofile[[`rhyme${j}`]] = item 111 | } 112 | rcont.sort() 113 | themes.label(theme, 'right', task, rcont.join(', ')) 114 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 115 | if (config.usage) reset ? console.log(`Timestamp expired, reset usage limits.\n${config.rbrain.date.remain}/${config.rbrain.date.limit} requests remaining this hour.`) : console.log(`${config.rbrain.date.remain}/${config.rbrain.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 116 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 117 | }) 118 | } else throw new Error(`Reached this hour's usage limit of ${config.rbrain.date.limit}.`) 119 | } 120 | -------------------------------------------------------------------------------- /src/commands/urban.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../themes') 3 | const tools = require('../tools') 4 | 5 | const _ = require('lodash') 6 | const http = require('good-guy-http')() 7 | const noon = require('noon') 8 | 9 | const CFILE = `${process.env.HOME}/.leximaven.noon` 10 | 11 | exports.command = 'urban ' 12 | exports.aliases = ['urb', 'slang'] 13 | exports.desc = 'Urban Dictionary definitions' 14 | exports.builder = { 15 | out: { 16 | alias: 'o', 17 | desc: 'Write cson, json, noon, plist, yaml, xml', 18 | default: '', 19 | type: 'string' 20 | }, 21 | force: { 22 | alias: 'f', 23 | desc: 'Force overwriting outfile', 24 | default: false, 25 | type: 'boolean' 26 | }, 27 | save: { 28 | alias: 's', 29 | desc: 'Save flags to config file', 30 | default: false, 31 | type: 'boolean' 32 | }, 33 | limit: { 34 | alias: 'l', 35 | desc: 'Limit number of results', 36 | default: 5, 37 | type: 'number' 38 | } 39 | } 40 | exports.handler = (argv) => { 41 | tools.checkConfig(CFILE) 42 | let config = noon.load(CFILE) 43 | const userConfig = { 44 | urban: { 45 | limit: argv.l 46 | } 47 | } 48 | if (config.merge) config = _.merge({}, config, userConfig) 49 | if (argv.s && config.merge) noon.save(CFILE, config) 50 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 51 | const theme = themes.loadTheme(config.theme) 52 | if (config.verbose) themes.label(theme, 'down', 'Urban Dictionary') 53 | const ucont = [] 54 | ucont.push(argv.query) 55 | if (argv._.length > 1) { 56 | for (let i = 0; i <= argv._.length - 1; i++) { 57 | if (argv._[i] !== 'urban' && argv._[i] !== 'urb' && argv._[i] !== 'slang') ucont.push(argv._[i]) 58 | } 59 | } 60 | let words = '' 61 | if (ucont.length > 1) { 62 | words = ucont.join('+') 63 | } else { 64 | words = ucont[0] 65 | } 66 | let url = `http://api.urbandictionary.com/v0/define?term=${words}` 67 | url = encodeURI(url) 68 | const tofile = { 69 | type: 'urban', 70 | source: 'http://www.urbandictionary.com', 71 | url 72 | } 73 | http({ url }, (error, response) => { 74 | if (!error && response.statusCode === 200) { 75 | const body = JSON.parse(response.body) 76 | const limit = config.urban.limit 77 | const list = body.list.slice(0, limit) 78 | for (let i = 0; i <= list.length - 1; i++) { 79 | const result = list[i] 80 | themes.label(theme, 'down', 'Definition', result.definition) 81 | tofile[[`definition${i}`]] = result.definition 82 | } 83 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 84 | } else { 85 | throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 86 | } 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /src/commands/wordmap.js: -------------------------------------------------------------------------------- 1 | const themes = require('../themes') 2 | const tools = require('../tools') 3 | 4 | const _ = require('lodash') 5 | const child = require('child_process') 6 | const fs = require('fs') 7 | const noon = require('noon') 8 | 9 | const CFILE = `${process.env.HOME}/.leximaven.noon` 10 | 11 | exports.command = 'wordmap ' 12 | exports.aliases = ['map', 'wm'] 13 | exports.desc = 'Maps of word info' 14 | exports.builder = { 15 | limit: { 16 | alias: 'l', 17 | desc: 'Limits the number of results', 18 | default: 1, 19 | type: 'number' 20 | }, 21 | save: { 22 | alias: 's', 23 | desc: 'Save flags to config file', 24 | default: false, 25 | type: 'boolean' 26 | } 27 | } 28 | exports.handler = (argv) => { 29 | tools.checkConfig(CFILE) 30 | let config = noon.load(CFILE) 31 | const userConfig = { 32 | wordmap: { 33 | limit: argv.l 34 | } 35 | } 36 | if (config.merge) config = _.merge({}, config, userConfig) 37 | if (argv.s && config.merge) noon.save(CFILE, config) 38 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 39 | const theme = themes.loadTheme(config.theme) 40 | if (config.verbose) themes.label(theme, 'down', 'Wordmap') 41 | const word = argv.word 42 | const l = argv.l 43 | let bin = '' 44 | let dirExists = null 45 | try { 46 | fs.statSync('bin/leximaven.js') 47 | dirExists = true 48 | } catch (e) { 49 | if (e.code === 'ENOENT') dirExists = false 50 | } 51 | dirExists ? bin = 'bin/leximaven.js' : bin = `${process.env.NODE_PATH}/leximaven/bin/leximaven.js` 52 | child.spawnSync('node', [bin, 'rbrain', 'combine', `-m${l}`, `${word}`], { stdio: 'inherit' }) 53 | child.spawnSync('node', [bin, 'rbrain', 'info', `${word}`], { stdio: 'inherit' }) 54 | child.spawnSync('node', [bin, 'rbrain', 'rhyme', `-m${l}`, `${word}`], { stdio: 'inherit' }) 55 | child.spawnSync('node', [bin, 'wordnik', 'define', `-l${l}`, `${word}`], { stdio: 'inherit' }) 56 | child.spawnSync('node', [bin, 'wordnik', 'example', `-l${l}`, `${word}`], { stdio: 'inherit' }) 57 | child.spawnSync('node', [bin, 'wordnik', 'hyphen', `${word}`], { stdio: 'inherit' }) 58 | child.spawnSync('node', [bin, 'wordnik', 'origin', `${word}`], { stdio: 'inherit' }) 59 | child.spawnSync('node', [bin, 'wordnik', 'phrase', `-l${l}`, `${word}`], { stdio: 'inherit' }) 60 | child.spawnSync('node', [bin, 'wordnik', 'pronounce', `-l${l}`, `${word}`], { stdio: 'inherit' }) 61 | child.spawnSync('node', [bin, 'wordnik', 'relate', `-l${l}`, `${word}`], { stdio: 'inherit' }) 62 | child.spawnSync('node', [bin, 'acronym', `${word}`], { stdio: 'inherit' }) 63 | child.spawnSync('node', [bin, 'dmuse', `-m${l}`, `ml=${word}`], { stdio: 'inherit' }) 64 | child.spawnSync('node', [bin, 'onelook', `${word}`], { stdio: 'inherit' }) 65 | child.spawnSync('node', [bin, 'urban', `-l${l}`, `${word}`], { stdio: 'inherit' }) 66 | // child.spawnSync('node', [bin, 'anagram', `-t${l}`, `${word}`], { stdio: 'inherit' }) 67 | } 68 | -------------------------------------------------------------------------------- /src/commands/wordnik.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | exports.command = 'wordnik ' 3 | exports.aliases = ['wnik', 'wn'] 4 | exports.desc = 'Wordnik tasks' 5 | exports.builder = (yargs) => yargs.commandDir('wordnik') 6 | exports.handler = (argv) => {} 7 | -------------------------------------------------------------------------------- /src/commands/wordnik/define.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const chalk = require('chalk') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | 11 | const CFILE = `${process.env.HOME}/.leximaven.noon` 12 | 13 | exports.command = 'define ' 14 | exports.aliases = ['def'] 15 | exports.desc = 'Wordnik definitions' 16 | exports.builder = { 17 | out: { 18 | alias: 'o', 19 | desc: 'Write cson, json, noon, plist, yaml, xml', 20 | default: '', 21 | type: 'string' 22 | }, 23 | force: { 24 | alias: 'f', 25 | desc: 'Force overwriting outfile', 26 | default: false, 27 | type: 'boolean' 28 | }, 29 | save: { 30 | alias: 's', 31 | desc: 'Save flags to config file', 32 | default: false, 33 | type: 'boolean' 34 | }, 35 | limit: { 36 | alias: 'l', 37 | desc: 'Limit number of results', 38 | default: 5, 39 | type: 'number' 40 | }, 41 | canon: { 42 | alias: 'c', 43 | desc: 'Use canonical', 44 | default: false, 45 | type: 'boolean' 46 | }, 47 | defdict: { 48 | alias: 'd', 49 | desc: "CSV list of dictionaries or 'all'", 50 | default: 'all', 51 | type: 'string' 52 | }, 53 | part: { 54 | alias: 'p', 55 | desc: 'CSV list of parts of speech. See http://developer.wordnik.com/docs.html for list of parts.', 56 | default: '', 57 | type: 'string' 58 | } 59 | } 60 | exports.handler = (argv) => { 61 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 62 | tools.checkConfig(CFILE) 63 | let config = noon.load(CFILE) 64 | let proceed = false 65 | let reset = false 66 | const stamp = new Date(config.wordnik.date.stamp) 67 | const minutes = df.differenceInMinutes(new Date(), stamp) 68 | const checkStamp = tools.limitWordnik(config) 69 | config = checkStamp[0] 70 | proceed = checkStamp[1] 71 | reset = checkStamp[2] 72 | if (proceed) { 73 | const userConfig = { 74 | wordnik: { 75 | define: { 76 | canon: argv.c, 77 | limit: argv.l, 78 | defdict: argv.d, 79 | part: argv.p 80 | } 81 | } 82 | } 83 | if (config.merge) config = _.merge({}, config, userConfig) 84 | if (argv.s && config.merge) noon.save(CFILE, config) 85 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 86 | const theme = themes.loadTheme(config.theme) 87 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 88 | const word = argv.word 89 | const task = 'definitions' 90 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 91 | const apikey = process.env.WORDNIK 92 | const uri = `${prefix}${word}/${task}?` 93 | const pcont = [] 94 | pcont.push(`useCanonical=${config.wordnik.define.canon}&`) 95 | pcont.push(`sourceDictionaries=${config.wordnik.define.defdict}&`) 96 | pcont.push('includeRelated=false&') 97 | pcont.push('includeTags=false&') 98 | pcont.push(`limit=${config.wordnik.define.limit}&`) 99 | pcont.push(`partOfSpeech=${config.wordnik.define.part}&`) 100 | pcont.push(`api_key=${apikey}`) 101 | const rest = pcont.join('') 102 | let url = `${uri}${rest}` 103 | url = encodeURI(url) 104 | const tofile = { 105 | type: 'definition', 106 | source: 'http://www.wordnik.com' 107 | } 108 | const cstyle = _.get(chalk, theme.connector.style) 109 | const ctstyle = _.get(chalk, theme.content.style) 110 | const uline = _.get(chalk, `${theme.content.style}.underline`) 111 | const conn = cstyle(theme.connector.str) 112 | http({ url }, (error, response) => { 113 | if (!error && response.statusCode === 200) { 114 | if (response.headers['x-gg-state'] === 'cached') { 115 | config.wordnik.date.remain++ 116 | noon.save(CFILE, config) 117 | if (config.usage) console.log('Cached response, not decrementing usage.') 118 | } 119 | const list = JSON.parse(response.body) 120 | for (let i = 0; i <= list.length - 1; i++) { 121 | const item = list[i] 122 | const icont = [] 123 | icont.push(ctstyle(`${item.text} `)) 124 | icont.push(uline(item.partOfSpeech)) 125 | icont.push(conn) 126 | icont.push(ctstyle(item.sourceDictionary)) 127 | themes.label(theme, 'right', 'Definition', icont.join('')) 128 | tofile[[`text${i}`]] = item.text 129 | tofile[[`deftype${i}`]] = item.partOfSpeech 130 | tofile[[`source${i}`]] = item.sourceDictionary 131 | } 132 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 133 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 134 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 135 | }) 136 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 137 | } 138 | -------------------------------------------------------------------------------- /src/commands/wordnik/example.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const http = require('good-guy-http')() 8 | const noon = require('noon') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | exports.command = 'example ' 13 | exports.aliases = ['ex'] 14 | exports.desc = 'Wordnik examples' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | }, 28 | save: { 29 | alias: 's', 30 | desc: 'Save flags to config file', 31 | default: false, 32 | type: 'boolean' 33 | }, 34 | limit: { 35 | alias: 'l', 36 | desc: 'Limit number of results', 37 | default: 5, 38 | type: 'number' 39 | }, 40 | canon: { 41 | alias: 'c', 42 | desc: 'Use canonical', 43 | default: false, 44 | type: 'boolean' 45 | }, 46 | skip: { 47 | alias: 'k', 48 | desc: 'Number of results to skip', 49 | default: 0, 50 | type: 'number' 51 | } 52 | } 53 | exports.handler = (argv) => { 54 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 55 | tools.checkConfig(CFILE) 56 | let config = noon.load(CFILE) 57 | let proceed = false 58 | let reset = false 59 | const stamp = new Date(config.wordnik.date.stamp) 60 | const minutes = df.differenceInMinutes(new Date(), stamp) 61 | const checkStamp = tools.limitWordnik(config) 62 | config = checkStamp[0] 63 | proceed = checkStamp[1] 64 | reset = checkStamp[2] 65 | if (proceed) { 66 | const userConfig = { 67 | wordnik: { 68 | example: { 69 | canon: argv.c, 70 | limit: argv.l, 71 | skip: argv.k 72 | } 73 | } 74 | } 75 | if (config.merge) config = _.merge({}, config, userConfig) 76 | if (argv.s && config.merge) noon.save(CFILE, config) 77 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 78 | const theme = themes.loadTheme(config.theme) 79 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 80 | const word = argv.word 81 | const task = 'examples' 82 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 83 | const apikey = process.env.WORDNIK 84 | const uri = `${prefix}${word}/${task}?` 85 | const pcont = [] 86 | pcont.push(`useCanonical=${config.wordnik.example.canon}&`) 87 | pcont.push('includeDuplicates=false&') 88 | pcont.push(`limit=${config.wordnik.example.limit}&`) 89 | !config.wordnik.example.skip ? pcont.push('skip=0&') : pcont.push(`skip=${config.wordnik.example.skip}&`) 90 | pcont.push(`api_key=${apikey}`) 91 | const rest = pcont.join('') 92 | let url = `${uri}${rest}` 93 | url = encodeURI(url) 94 | const tofile = { 95 | type: 'example', 96 | source: 'http://www.wordnik.com' 97 | } 98 | http({ url }, (error, response) => { 99 | if (!error && response.statusCode === 200) { 100 | if (response.headers['x-gg-state'] === 'cached') { 101 | config.wordnik.date.remain++ 102 | noon.save(CFILE, config) 103 | if (config.usage) console.log('Cached response, not decrementing usage.') 104 | } 105 | const body = JSON.parse(response.body) 106 | const list = body.examples 107 | for (let i = 0; i <= list.length - 1; i++) { 108 | const item = list[i] 109 | themes.label(theme, 'right', 'Example', item.text) 110 | tofile[[`example${i}`]] = item.text 111 | } 112 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 113 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 114 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 115 | }) 116 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 117 | } 118 | -------------------------------------------------------------------------------- /src/commands/wordnik/hyphen.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const chalk = require('chalk') 7 | const df = require('date-fns') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | 11 | const CFILE = `${process.env.HOME}/.leximaven.noon` 12 | 13 | exports.command = 'hyphen ' 14 | exports.aliases = ['hyphenate', 'hy'] 15 | exports.desc = 'Wordnik hyphenations' 16 | exports.builder = { 17 | out: { 18 | alias: 'o', 19 | desc: 'Write cson, json, noon, plist, yaml, xml', 20 | default: '', 21 | type: 'string' 22 | }, 23 | force: { 24 | alias: 'f', 25 | desc: 'Force overwriting outfile', 26 | default: false, 27 | type: 'boolean' 28 | }, 29 | save: { 30 | alias: 's', 31 | desc: 'Save flags to config file', 32 | default: false, 33 | type: 'boolean' 34 | }, 35 | limit: { 36 | alias: 'l', 37 | desc: 'Limit number of results', 38 | default: 5, 39 | type: 'number' 40 | }, 41 | canon: { 42 | alias: 'c', 43 | desc: 'Use canonical', 44 | default: false, 45 | type: 'boolean' 46 | }, 47 | dict: { 48 | alias: 'd', 49 | desc: 'Source dictionary ahd, century, wiktionary, webster, wordnet', 50 | default: 'all', 51 | type: 'string' 52 | } 53 | } 54 | exports.handler = (argv) => { 55 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 56 | tools.checkConfig(CFILE) 57 | let config = noon.load(CFILE) 58 | let proceed = false 59 | let reset = false 60 | const stamp = new Date(config.wordnik.date.stamp) 61 | const minutes = df.differenceInMinutes(new Date(), stamp) 62 | const checkStamp = tools.limitWordnik(config) 63 | config = checkStamp[0] 64 | proceed = checkStamp[1] 65 | reset = checkStamp[2] 66 | if (proceed) { 67 | const userConfig = { 68 | wordnik: { 69 | hyphen: { 70 | canon: argv.c, 71 | dict: argv.d, 72 | limit: argv.l 73 | } 74 | } 75 | } 76 | if (config.merge) config = _.merge({}, config, userConfig) 77 | if (argv.s && config.merge) noon.save(CFILE, config) 78 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 79 | const theme = themes.loadTheme(config.theme) 80 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 81 | const word = argv.word 82 | const task = 'hyphenation' 83 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 84 | const apikey = process.env.WORDNIK 85 | const uri = `${prefix}${word}/${task}?` 86 | const pcont = [] 87 | pcont.push(`useCanonical=${config.wordnik.hyphen.canon}&`) 88 | if (argv.d !== 'all') pcont.push(`sourceDictionary=${config.wordnik.hyphen.dict}&`) 89 | pcont.push(`limit=${config.wordnik.hyphen.limit}&`) 90 | pcont.push(`api_key=${apikey}`) 91 | const rest = pcont.join('') 92 | let url = `${uri}${rest}` 93 | url = encodeURI(url) 94 | const tofile = { 95 | type: 'hyphenation', 96 | source: 'http://www.wordnik.com' 97 | } 98 | const ctstyle = _.get(chalk, theme.content.style) 99 | http({ url }, (error, response) => { 100 | if (!error && response.statusCode === 200) { 101 | if (response.headers['x-gg-state'] === 'cached') { 102 | config.wordnik.date.remain++ 103 | noon.save(CFILE, config) 104 | if (config.usage) console.log('Cached response, not decrementing usage.') 105 | } 106 | const list = JSON.parse(response.body) 107 | const hcont = [] 108 | for (let i = 0; i <= list.length - 1; i++) { 109 | const item = list[i] 110 | if (item.type === 'stress') { 111 | hcont.push(`${chalk.red.bold(item.text)}`) 112 | tofile[[`stress${i}`]] = item.text 113 | } else if (item.type === 'secondary stress') { 114 | hcont.push(ctstyle(item.text)) 115 | tofile[[`secondary${i}`]] = item.text 116 | } else { 117 | hcont.push(ctstyle(item.text)) 118 | tofile[[`syllable${i}`]] = item.text 119 | } 120 | if (i < list.length - 1) hcont.push(ctstyle('-')) 121 | } 122 | themes.label(theme, 'right', 'Hyphenation', hcont.join('')) 123 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 124 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 125 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 126 | }) 127 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 128 | } 129 | -------------------------------------------------------------------------------- /src/commands/wordnik/origin.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const chalk = require('chalk') 7 | const df = require('date-fns') 8 | const http = require('good-guy-http')() 9 | const noon = require('noon') 10 | const xml2js = require('xml2js') 11 | 12 | const CFILE = `${process.env.HOME}/.leximaven.noon` 13 | 14 | exports.command = 'origin ' 15 | exports.aliases = ['or', 'etymology'] 16 | exports.desc = 'Wordnik etymologies' 17 | exports.builder = { 18 | out: { 19 | alias: 'o', 20 | desc: 'Write cson, json, noon, plist, yaml, xml', 21 | default: '', 22 | type: 'string' 23 | }, 24 | force: { 25 | alias: 'f', 26 | desc: 'Force overwriting outfile', 27 | default: false, 28 | type: 'boolean' 29 | }, 30 | save: { 31 | alias: 's', 32 | desc: 'Save flags to config file', 33 | default: false, 34 | type: 'boolean' 35 | }, 36 | canon: { 37 | alias: 'c', 38 | desc: 'Use canonical', 39 | default: false, 40 | type: 'boolean' 41 | } 42 | } 43 | exports.handler = (argv) => { 44 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 45 | tools.checkConfig(CFILE) 46 | let config = noon.load(CFILE) 47 | let proceed = false 48 | let reset = false 49 | const stamp = new Date(config.wordnik.date.stamp) 50 | const minutes = df.differenceInMinutes(new Date(), stamp) 51 | const checkStamp = tools.limitWordnik(config) 52 | config = checkStamp[0] 53 | proceed = checkStamp[1] 54 | reset = checkStamp[2] 55 | if (proceed) { 56 | const userConfig = { 57 | wordnik: { 58 | origin: { 59 | canon: argv.c 60 | } 61 | } 62 | } 63 | if (config.merge) config = _.merge({}, config, userConfig) 64 | if (argv.s && config.merge) noon.save(CFILE, config) 65 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 66 | const theme = themes.loadTheme(config.theme) 67 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 68 | const word = argv.word 69 | const task = 'etymologies' 70 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 71 | const apikey = process.env.WORDNIK 72 | const uri = `${prefix}${word}/${task}?` 73 | const pcont = [] 74 | pcont.push(`useCanonical=${config.wordnik.origin.canon}&`) 75 | pcont.push(`api_key=${apikey}`) 76 | const rest = pcont.join('') 77 | let url = `${uri}${rest}` 78 | url = encodeURI(url) 79 | const parser = new xml2js.Parser() 80 | const tofile = { 81 | type: 'etymology', 82 | source: 'http://www.wordnik.com' 83 | } 84 | const ctstyle = _.get(chalk, theme.content.style) 85 | http({ url }, (error, response) => { 86 | if (!error && response.statusCode === 200) { 87 | if (response.headers['x-gg-state'] === 'cached') { 88 | config.wordnik.date.remain++ 89 | noon.save(CFILE, config) 90 | if (config.usage) console.log('Cached response, not decrementing usage.') 91 | } 92 | const resp = JSON.parse(response.body) 93 | const origin = resp[0] 94 | parser.parseString(origin, (err, result) => { 95 | if (!err) { 96 | const root = result.ety 97 | const content = root._ 98 | let ets = root.ets 99 | ets = ets.join(', ') 100 | themes.label(theme, 'right', 'Etymology', ctstyle(`${content} ${ets}`)) 101 | tofile.etymology = content 102 | tofile.origin = ets 103 | } else { 104 | throw new Error(err) 105 | } 106 | }) 107 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 108 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 109 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 110 | }) 111 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 112 | } 113 | -------------------------------------------------------------------------------- /src/commands/wordnik/phrase.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const http = require('good-guy-http')() 8 | const noon = require('noon') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | exports.command = 'phrase ' 13 | exports.aliases = ['ph', 'ngram'] 14 | exports.desc = 'Wordnik bi-gram phrases' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | }, 28 | save: { 29 | alias: 's', 30 | desc: 'Save flags to config file', 31 | default: false, 32 | type: 'boolean' 33 | }, 34 | limit: { 35 | alias: 'l', 36 | desc: 'Limit number of results', 37 | default: 5, 38 | type: 'number' 39 | }, 40 | canon: { 41 | alias: 'c', 42 | desc: 'Use canonical', 43 | default: false, 44 | type: 'boolean' 45 | }, 46 | weight: { 47 | alias: 'w', 48 | desc: 'Minimum weighted mutual info', 49 | default: 13, 50 | type: 'number' 51 | } 52 | } 53 | exports.handler = (argv) => { 54 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 55 | tools.checkConfig(CFILE) 56 | let config = noon.load(CFILE) 57 | let proceed = false 58 | let reset = false 59 | const stamp = new Date(config.wordnik.date.stamp) 60 | const minutes = df.differenceInMinutes(new Date(), stamp) 61 | const checkStamp = tools.limitWordnik(config) 62 | config = checkStamp[0] 63 | proceed = checkStamp[1] 64 | reset = checkStamp[2] 65 | if (proceed) { 66 | const userConfig = { 67 | wordnik: { 68 | phrase: { 69 | canon: argv.c, 70 | limit: argv.l, 71 | weight: argv.w 72 | } 73 | } 74 | } 75 | if (config.merge) config = _.merge({}, config, userConfig) 76 | if (argv.s && config.merge) noon.save(CFILE, config) 77 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 78 | const theme = themes.loadTheme(config.theme) 79 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 80 | const word = argv.word 81 | const task = 'phrases' 82 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 83 | const apikey = process.env.WORDNIK 84 | const uri = `${prefix}${word}/${task}?` 85 | const pcont = [] 86 | pcont.push(`useCanonical=${argv.c}&`) 87 | pcont.push(`limit=${argv.l}&`) 88 | pcont.push(`wlmi=${argv.w}&`) 89 | pcont.push(`api_key=${apikey}`) 90 | const rest = pcont.join('') 91 | let url = `${uri}${rest}` 92 | url = encodeURI(url) 93 | themes.label(theme, 'down', 'Bi-gram phrases') 94 | const tofile = { 95 | type: 'phrase', 96 | source: 'http://www.wordnik.com' 97 | } 98 | http({ url }, (error, response) => { 99 | if (!error && response.statusCode === 200) { 100 | if (response.headers['x-gg-state'] === 'cached') { 101 | config.wordnik.date.remain++ 102 | noon.save(CFILE, config) 103 | if (config.usage) console.log('Cached response, not decrementing usage.') 104 | } 105 | const list = JSON.parse(response.body) 106 | for (let i = 0; i <= list.length - 1; i++) { 107 | const item = list[i] 108 | console.log(`${item.gram1} ${item.gram2}`) 109 | tofile[[`agram${i}`]] = item.gram1 110 | tofile[[`bgram${i}`]] = item.gram2 111 | } 112 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 113 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 114 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 115 | }) 116 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 117 | } 118 | -------------------------------------------------------------------------------- /src/commands/wordnik/pronounce.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const http = require('good-guy-http')() 8 | const noon = require('noon') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | exports.command = 'pronounce ' 13 | exports.aliases = ['pr'] 14 | exports.desc = 'Wordnik pronunciations' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | }, 28 | save: { 29 | alias: 's', 30 | desc: 'Save flags to config file', 31 | default: false, 32 | type: 'boolean' 33 | }, 34 | limit: { 35 | alias: 'l', 36 | desc: 'Limit number of results', 37 | default: 5, 38 | type: 'number' 39 | }, 40 | canon: { 41 | alias: 'c', 42 | desc: 'Use canonical', 43 | default: false, 44 | type: 'boolean' 45 | }, 46 | dict: { 47 | alias: 'd', 48 | desc: 'Dictionary: ahd, century, cmu, macmillan, wiktionary, webster, wordnet', 49 | default: '', 50 | type: 'string' 51 | }, 52 | type: { 53 | alias: 't', 54 | desc: 'Type: ahd, arpabet, gcide-diacritical, ipa', 55 | default: '', 56 | type: 'string' 57 | } 58 | } 59 | exports.handler = (argv) => { 60 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 61 | tools.checkConfig(CFILE) 62 | let config = noon.load(CFILE) 63 | let proceed = false 64 | let reset = false 65 | const stamp = new Date(config.wordnik.date.stamp) 66 | const minutes = df.differenceInMinutes(new Date(), stamp) 67 | const checkStamp = tools.limitWordnik(config) 68 | config = checkStamp[0] 69 | proceed = checkStamp[1] 70 | reset = checkStamp[2] 71 | if (proceed) { 72 | const userConfig = { 73 | wordnik: { 74 | pronounce: { 75 | canon: argv.c, 76 | dict: argv.d, 77 | type: argv.t, 78 | limit: argv.l 79 | } 80 | } 81 | } 82 | if (config.merge) config = _.merge({}, config, userConfig) 83 | if (argv.s && config.merge) noon.save(CFILE, config) 84 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 85 | const theme = themes.loadTheme(config.theme) 86 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 87 | const word = argv.word 88 | const task = 'pronunciations' 89 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 90 | const apikey = process.env.WORDNIK 91 | const uri = `${prefix}${word}/${task}?` 92 | const pcont = [] 93 | pcont.push(`useCanonical=${config.wordnik.pronounce.canon}&`) 94 | if (config.wordnik.pronounce.dict !== '') pcont.push(`sourceDictionary=${config.wordnik.pronounce.dict}&`) 95 | if (config.wordnik.pronounce.type !== '') pcont.push(`typeFormat=${config.wordnik.pronounce.type}&`) 96 | pcont.push(`limit=${config.wordnik.pronounce.limit}&`) 97 | pcont.push(`api_key=${apikey}`) 98 | const rest = pcont.join('') 99 | let url = `${uri}${rest}` 100 | url = encodeURI(url) 101 | themes.label(theme, 'down', 'Pronunciations') 102 | const tofile = { 103 | type: 'pronunciation', 104 | source: 'http://www.wordnik.com' 105 | } 106 | tofile.word = word 107 | http({ url }, (error, response) => { 108 | if (!error && response.statusCode === 200) { 109 | if (response.headers['x-gg-state'] === 'cached') { 110 | config.wordnik.date.remain++ 111 | noon.save(CFILE, config) 112 | if (config.usage) console.log('Cached response, not decrementing usage.') 113 | } 114 | const list = JSON.parse(response.body) 115 | for (let i = 0; i <= list.length - 1; i++) { 116 | const item = list[i] 117 | themes.label(theme, 'right', word, `${item.raw} - Type - ${item.rawType}`) 118 | tofile[[`pronunciation${i}`]] = item.raw 119 | tofile[[`type${i}`]] = item.rawType 120 | } 121 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 122 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 123 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 124 | }) 125 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 126 | } 127 | -------------------------------------------------------------------------------- /src/commands/wordnik/relate.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const themes = require('../../themes') 3 | const tools = require('../../tools') 4 | 5 | const _ = require('lodash') 6 | const df = require('date-fns') 7 | const http = require('good-guy-http')() 8 | const noon = require('noon') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | exports.command = 'relate ' 13 | exports.aliases = ['related', 'rel'] 14 | exports.desc = 'Wordnik related words' 15 | exports.builder = { 16 | out: { 17 | alias: 'o', 18 | desc: 'Write cson, json, noon, plist, yaml, xml', 19 | default: '', 20 | type: 'string' 21 | }, 22 | force: { 23 | alias: 'f', 24 | desc: 'Force overwriting outfile', 25 | default: false, 26 | type: 'boolean' 27 | }, 28 | save: { 29 | alias: 's', 30 | desc: 'Save flags to config file', 31 | default: false, 32 | type: 'boolean' 33 | }, 34 | limit: { 35 | alias: 'l', 36 | desc: 'Limit results = require(type option', 37 | default: 10, 38 | type: 'number' 39 | }, 40 | canon: { 41 | alias: 'c', 42 | desc: 'Use canonical', 43 | default: false, 44 | type: 'boolean' 45 | }, 46 | type: { 47 | alias: 't', 48 | desc: 'Relationship types to limit', 49 | default: '', 50 | type: 'string' 51 | } 52 | } 53 | exports.handler = (argv) => { 54 | if (process.env.WORDNIK === undefined) throw new Error('Put an API key in environment variable WORDNIK per documentation.') 55 | tools.checkConfig(CFILE) 56 | let config = noon.load(CFILE) 57 | let proceed = false 58 | let reset = false 59 | const stamp = new Date(config.wordnik.date.stamp) 60 | const minutes = df.differenceInMinutes(new Date(), stamp) 61 | const checkStamp = tools.limitWordnik(config) 62 | config = checkStamp[0] 63 | proceed = checkStamp[1] 64 | reset = checkStamp[2] 65 | if (proceed) { 66 | const userConfig = { 67 | wordnik: { 68 | relate: { 69 | canon: argv.c, 70 | type: argv.t, 71 | limit: argv.l 72 | } 73 | } 74 | } 75 | if (config.merge) config = _.merge({}, config, userConfig) 76 | if (argv.s && config.merge) noon.save(CFILE, config) 77 | if (argv.s && !config.merge) throw new Error("Can't save user config, set option merge to true.") 78 | const theme = themes.loadTheme(config.theme) 79 | if (config.verbose) themes.label(theme, 'down', 'Wordnik') 80 | const word = argv.word 81 | const task = 'relatedWords' 82 | const prefix = 'http://api.wordnik.com:80/v4/word.json/' 83 | const apikey = process.env.WORDNIK 84 | const uri = `${prefix}${word}/${task}?` 85 | const pcont = [] 86 | pcont.push(`useCanonical=${config.wordnik.relate.canon}&`) 87 | if (config.wordnik.relate.type !== '') pcont.push(`relationshipTypes=${config.wordnik.relate.type}&`) 88 | pcont.push(`limitPerRelationshipType=${config.wordnik.relate.limit}&`) 89 | pcont.push(`api_key=${apikey}`) 90 | const rest = pcont.join('') 91 | let url = `${uri}${rest}` 92 | url = encodeURI(url) 93 | themes.label(theme, 'down', 'Related words') 94 | const tofile = { 95 | type: 'related words', 96 | source: 'http://www.wordnik.com' 97 | } 98 | tofile.word = word 99 | http({ url }, (error, response) => { 100 | if (!error && response.statusCode === 200) { 101 | if (response.headers['x-gg-state'] === 'cached') { 102 | config.wordnik.date.remain++ 103 | noon.save(CFILE, config) 104 | if (config.usage) console.log('Cached response, not decrementing usage.') 105 | } 106 | const list = JSON.parse(response.body) 107 | for (let i = 0; i <= list.length - 1; i++) { 108 | const item = list[i] 109 | themes.label(theme, 'right', item.relationshipType, `${item.words.join(', ')}`) 110 | tofile[[`type${i}`]] = item.relationshipType 111 | tofile[[`words${i}`]] = item.words.join(', ') 112 | } 113 | if (argv.o) tools.outFile(argv.o, argv.f, tofile) 114 | if (config.usage) reset ? console.log(`Timestamp expired, not decrementing usage.\n${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour.`) : console.log(`${config.wordnik.date.remain}/${config.wordnik.date.limit} requests remaining this hour, will reset in ${59 - minutes} minutes.`) 115 | } else throw new Error(`HTTP ${error.statusCode}: ${error.reponse.body}`) 116 | }) 117 | } else throw new Error(`Reached this hour's usage limit of ${config.wordnik.date.limit}.`) 118 | } 119 | -------------------------------------------------------------------------------- /src/leximaven.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint max-len: 0, no-unused-expressions: 0 */ 3 | const chalk = require('chalk') 4 | const pkg = require('../package.json') 5 | const yargonaut = require('yargonaut') 6 | .style('bold.underline', 'Commands:') 7 | .style('bold.underline', 'Options:') 8 | .style('bold.cyan', 'boolean') 9 | .style('bold.yellow', 'string') 10 | .style('bold.magenta', 'number') 11 | .style('bold.blue', 'default:') 12 | .style('bold.green', 'aliases:') 13 | const yargs = require('yargs') 14 | yargs 15 | .commandDir('commands') 16 | .usage(`${chalk.yellow(`${yargonaut.asFont('leximaven', 'Small Slant')}`)}\n${chalk.bold.underline('Usage:')}\n$0 [options]`) 17 | .help('h') 18 | .alias('h', 'help') 19 | .option('v', { 20 | alias: 'verbose', 21 | type: 'boolean', 22 | desc: 'Verbose output' 23 | }) 24 | .version('V', 'Show current version', pkg.version) 25 | .alias('V', 'version') 26 | .global('v') 27 | .demand(1) 28 | .argv 29 | -------------------------------------------------------------------------------- /src/themes.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | const _ = require('lodash') 3 | const chalk = require('chalk') 4 | const fs = require('fs') 5 | const glob = require('glob') 6 | const noon = require('noon') 7 | 8 | let TDIR = null 9 | let themeDirExists = null 10 | try { 11 | fs.statSync('themes') 12 | themeDirExists = true 13 | } catch (e) { 14 | if (e.code === 'ENOENT') themeDirExists = false 15 | } 16 | themeDirExists ? TDIR = 'themes/' : TDIR = `${process.env.NODE_PATH}/leximaven/themes/` 17 | 18 | /** 19 | * The themes module provides useful repetitive theme tasks 20 | * @module Themes 21 | */ 22 | 23 | /** 24 | * Loads theme 25 | * @public 26 | * @param {string} theme The name of the theme 27 | * @return {Object} load The style to use 28 | */ 29 | exports.loadTheme = (theme) => { 30 | let dirExists = null 31 | let load = null 32 | try { 33 | fs.statSync('themes') 34 | dirExists = true 35 | } catch (e) { 36 | if (e.code === 'ENOENT') dirExists = false 37 | } 38 | const CFILE = `${process.env.HOME}/.leximaven.noon` 39 | const config = noon.load(CFILE) 40 | if (!dirExists && config.verbose) console.log(chalk.white(`${process.cwd()}/themes does not exist, falling back to ${process.env.NODE_PATH}/leximaven/themes.`)) 41 | load = noon.load(`${TDIR}${theme}.noon`) 42 | return load 43 | } 44 | 45 | /** 46 | * Gets themes for list command 47 | * @public 48 | * @return {Array} List of theme names 49 | */ 50 | exports.getThemes = () => { 51 | const list = [] 52 | let dirExists = null 53 | let files = [] 54 | try { 55 | fs.statSync('themes') 56 | dirExists = true 57 | } catch (e) { 58 | if (e.code === 'ENOENT') dirExists = false 59 | } 60 | const CFILE = `${process.env.HOME}/.leximaven.noon` 61 | const config = noon.load(CFILE) 62 | if (!dirExists && config.verbose) console.log(chalk.white(`${process.cwd()}/themes does not exist, falling back to ${process.env.NODE_PATH}/leximaven/themes.`)) 63 | files = glob.sync(`${TDIR}*.noon`) 64 | for (let i = 0; i <= files.length - 1; i++) { 65 | list.push(files[i].replace(/[a-z0-9/_.]*themes\//, '').replace(/\.noon/, '')) 66 | } 67 | return list 68 | } 69 | 70 | /** 71 | * Prints label, connector, and content 72 | * @public 73 | * @param {Object} theme The style to use 74 | * @param {string} direction 'down' or 'right' 75 | * @param {string} text The label text 76 | * @param {string} [content] The text the label points at 77 | * @return {string} The stylized string to log 78 | */ 79 | exports.label = (theme, direction, text, content) => { 80 | const pstyle = _.get(chalk, theme.prefix.style) 81 | const tstyle = _.get(chalk, theme.text.style) 82 | const sstyle = _.get(chalk, theme.suffix.style) 83 | const cnstyle = _.get(chalk, theme.connector.style) 84 | const ctstyle = _.get(chalk, theme.content.style) 85 | let label = `${pstyle(theme.prefix.str)}${tstyle(text)}${sstyle(theme.suffix.str)}` 86 | if (direction === 'right') { 87 | content !== null && content !== undefined ? label = `${label}${cnstyle(theme.connector.str)}${ctstyle(content)}` : label = `${label}` 88 | } else if (direction === 'down') { 89 | content !== null && content !== undefined ? label = `${label}\n${cnstyle(theme.connector.str)}${ctstyle(content)}` : label = `${label}` 90 | } else { throw new Error("Unsupported label direction, use 'down' or 'right'.") } 91 | console.log(label) 92 | return label 93 | } 94 | -------------------------------------------------------------------------------- /src/tools.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const chalk = require('chalk') 3 | const df = require('date-fns') 4 | const fs = require('fs-extra') 5 | const noon = require('noon') 6 | const ts = require('term-size') 7 | const wrap = require('wrap-ansi') 8 | const xml2js = require('xml2js') 9 | 10 | const CFILE = `${process.env.HOME}/.leximaven.noon` 11 | 12 | /** 13 | * The tools module provides useful repetitive tasks 14 | * @module Utils 15 | */ 16 | 17 | /** 18 | * Onelook's API limit check 19 | * @param {Object} config The current config 20 | * @return {Array} Updated config, proceed boolean, and reset boolean 21 | */ 22 | exports.limitOnelook = (config) => { 23 | const c = config 24 | let proceed = false 25 | let reset = false 26 | const stamp = new Date(c.onelook.date.stamp) 27 | const hours = df.differenceInHours(new Date(), stamp) 28 | if (hours < 24) { 29 | c.onelook.date.remain-- 30 | } else if (hours >= 24) { 31 | reset = true 32 | c.onelook.date.stamp = new Date().toJSON() 33 | c.onelook.date.remain = c.onelook.date.limit 34 | c.onelook.date.remain-- 35 | } 36 | c.onelook.date.remain <= 0 ? c.onelook.date.remain = 0 : proceed = true 37 | noon.save(CFILE, c) 38 | return [c, proceed, reset] 39 | } 40 | 41 | /** 42 | * Datamuse's API limit check 43 | * @param {Object} config The current config 44 | * @return {Array} Updated config, proceed boolean, and reset boolean 45 | */ 46 | exports.limitDmuse = (config) => { 47 | const c = config 48 | let proceed = false 49 | let reset = false 50 | const stamp = new Date(c.dmuse.date.stamp) 51 | const hours = df.differenceInHours(new Date(), stamp) 52 | if (hours < 24) { 53 | c.dmuse.date.remain-- 54 | } else if (hours >= 24) { 55 | reset = true 56 | c.dmuse.date.stamp = new Date().toJSON() 57 | c.dmuse.date.remain = c.dmuse.date.limit 58 | c.dmuse.date.remain-- 59 | } 60 | c.dmuse.date.remain <= 0 ? c.dmuse.date.remain = 0 : proceed = true 61 | noon.save(CFILE, c) 62 | return [c, proceed, reset] 63 | } 64 | 65 | /** 66 | * Rhymebrain's API limit check 67 | * @param {Object} config The current config 68 | * @return {Array} Updated config, proceed boolean, and reset boolean 69 | */ 70 | exports.limitRbrain = (config) => { 71 | const c = config 72 | let proceed = false 73 | let reset = false 74 | const stamp = new Date(c.rbrain.date.stamp) 75 | const minutes = df.differenceInMinutes(new Date(), stamp) 76 | if (minutes < 60) { 77 | c.rbrain.date.remain-- 78 | } else if (minutes >= 60) { 79 | reset = true 80 | c.rbrain.date.stamp = new Date().toJSON() 81 | c.rbrain.date.remain = c.rbrain.date.limit 82 | c.rbrain.date.remain-- 83 | } 84 | c.rbrain.date.remain <= 0 ? c.rbrain.date.remain = 0 : proceed = true 85 | noon.save(CFILE, c) 86 | return [c, proceed, reset] 87 | } 88 | 89 | /** 90 | * Wordnik's API limit check 91 | * @param {Object} config The current config 92 | * @return {Array} Updated config, proceed boolean, and reset boolean 93 | */ 94 | exports.limitWordnik = (config) => { 95 | const c = config 96 | let proceed = false 97 | let reset = false 98 | const stamp = new Date(c.wordnik.date.stamp) 99 | const minutes = df.differenceInMinutes(new Date(), stamp) 100 | if (minutes < 60) { 101 | c.wordnik.date.remain-- 102 | } else if (minutes >= 60) { 103 | reset = true 104 | c.wordnik.date.stamp = new Date().toJSON() 105 | c.wordnik.date.remain = c.wordnik.date.limit 106 | c.wordnik.date.remain-- 107 | } 108 | c.wordnik.date.remain <= 0 ? c.wordnik.date.remain = 0 : proceed = true 109 | noon.save(CFILE, c) 110 | return [c, proceed, reset] 111 | } 112 | 113 | /** 114 | * Checks if a file exists 115 | * @private 116 | * @param {string} path The filename to check. 117 | * @return {boolean} fileExists 118 | */ 119 | function checkOutfile (path) { 120 | let fileExists = null 121 | try { 122 | fs.statSync(path) 123 | fileExists = true 124 | } catch (e) { 125 | if (e.code === 'ENOENT') fileExists = false 126 | } 127 | return fileExists 128 | } 129 | 130 | /** 131 | * Converts string to boolean 132 | * @public 133 | * @param {string} value 134 | * @return {boolean} v 135 | */ 136 | exports.checkBoolean = (value) => { 137 | let v = value 138 | if (v === 'true') v = true 139 | if (v === 'false') v = false 140 | return v 141 | } 142 | 143 | /** 144 | * Converts a boolean to a 0 or 1 145 | * @param {boolean} value A boolean value 146 | * @return {integer} 0 or 1 147 | */ 148 | exports.boolToBin = (value) => { 149 | let r = null 150 | value ? r = 1 : r = 0 151 | return r 152 | } 153 | 154 | /** 155 | * Checks if config exists. If not, prints init message and exits with error code. 156 | * @public 157 | * @param {string} file Configuration filepath 158 | */ 159 | exports.checkConfig = (file) => { 160 | try { 161 | fs.statSync(file) 162 | } catch (e) { 163 | if (e.code === 'ENOENT') throw new Error(`No config found at ${file}, run: 'leximaven config init'`) 164 | } 165 | return true 166 | } 167 | 168 | /** 169 | * Checks if object is a single string in an array 170 | * @public 171 | * @param {Object} obj Any object 172 | * @return {Object} Original object or extracted string 173 | */ 174 | exports.arrToStr = (obj) => { 175 | let fixed = null 176 | Array.isArray(obj) && obj.length === 1 && typeof obj[0] === 'string' ? fixed = obj[0] : fixed = obj 177 | return fixed 178 | } 179 | 180 | /** 181 | * Strips HTML from a string 182 | * @public 183 | * @param {string} string Text with HTML tags 184 | * @return {string} Plain text string 185 | */ 186 | exports.stripHTML = (string) => string.replace(/(<([^>]+)>)/ig, '') 187 | 188 | /** 189 | * Wraps blocks of text 190 | * @param {string} str Long string 191 | * @param {boolean} hard true, soft false 192 | * @param {boolean} wwrap true, column wrap false 193 | * @return {string} ANSI-wrapped string 194 | */ 195 | exports.wrapStr = (str, hard, wwrap) => { 196 | const termsize = ts() 197 | return wrap(str, termsize.columns, hard, wwrap) 198 | } 199 | 200 | /** 201 | * Handles data export to file. Supports cson, json, noon, plist, xml, yaml. 202 | * @public 203 | * @param {string} path The desired filepath and extension 204 | * @param {boolean} force Whether to force overwrite 205 | * @param {Object} tofile A numbered object of data points 206 | */ 207 | exports.outFile = (path, force, tofile) => { 208 | const match = path.match(/\.([a-z]*)$/i) 209 | const ext = match[1] 210 | const builder = new xml2js.Builder() 211 | if (ext === 'xml') { 212 | if (checkOutfile(path)) { 213 | if (force) { 214 | const xml = builder.buildObject(tofile) 215 | const fd = fs.openSync(path, 'w+') 216 | fs.writeSync(fd, xml) 217 | fs.closeSync(fd) 218 | console.log(chalk.white(`Overwrote ${path} with data.`)) 219 | } else console.log(chalk.white(`${path} exists, use -f to force overwrite.`)) 220 | } else { 221 | const xml = builder.buildObject(tofile) 222 | const fd = fs.openSync(path, 'w+') 223 | fs.writeSync(fd, xml) 224 | fs.closeSync(fd) 225 | console.log(chalk.white(`Wrote data to ${path}.`)) 226 | } 227 | } else if (ext === 'cson' || ext === 'json' || ext === 'noon' || ext === 'plist' || ext === 'yml' || ext === 'yaml') { 228 | if (checkOutfile(path)) { 229 | if (force) { 230 | noon.save(path, tofile) 231 | console.log(chalk.white(`Overwrote ${path} with data.`)) 232 | } else console.log(chalk.white(`${path} exists, use -f to force overwrite.`)) 233 | } else { 234 | noon.save(path, tofile) 235 | console.log(chalk.white(`Wrote data to ${path}.`)) 236 | } 237 | } else if (ext !== 'xml' || ext !== 'cson' || ext !== 'json' || ext !== 'noon' || ext !== 'plist' || ext !== 'yml' || ext !== 'yaml') throw new Error(`Format ${ext} not supported.`) 238 | } 239 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Get definitions for 'catharsis' 4 | leximaven wordnik define catharsis 5 | #Get antonyms for 'noise' 6 | leximaven wordnik relate --canon --type antonym noises 7 | # Pronounce 'quixotic' 8 | leximaven wordnik pronounce quixotic 9 | # Get etymology for 'special' 10 | leximaven wordnik origin special 11 | # Get words that sound like 'blue' 12 | leximaven datamuse get sl=blue 13 | # Get slang/colloquialisms for 'diesel' 14 | leximaven urban diesel 15 | # Get a wordmap for 'ubiquity' 16 | leximaven wordmap ubiquity 17 | -------------------------------------------------------------------------------- /themes/colonel.noon: -------------------------------------------------------------------------------- 1 | prefix 2 | str :: 3 | style bold.magenta 4 | text 5 | style bold.yellow 6 | content 7 | style white 8 | suffix 9 | str :: 10 | style bold.magenta 11 | connector 12 | str ||| 13 | style bold.yellow 14 | -------------------------------------------------------------------------------- /themes/markup.noon: -------------------------------------------------------------------------------- 1 | prefix 2 | str < 3 | style bold.blue 4 | text 5 | style bold.cyan 6 | content 7 | style white 8 | suffix 9 | str > 10 | style bold.blue 11 | connector 12 | str -> 13 | style bold.green 14 | -------------------------------------------------------------------------------- /themes/square.noon: -------------------------------------------------------------------------------- 1 | prefix 2 | str [ 3 | style bold.green 4 | text 5 | style bold.white 6 | content 7 | style white 8 | suffix 9 | str ] 10 | style bold.green 11 | connector 12 | str → 13 | style bold.cyan 14 | --------------------------------------------------------------------------------