├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build_docs.yml │ ├── nodejs_test.yml │ └── npmpublish.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── PROJECTS.md ├── conf.json ├── index.d.ts ├── index.js ├── objects ├── Files.js └── Mod.js ├── package.json ├── readme.md └── test └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = CRLF 5 | insert_final_newline = false 6 | indent_style = tab 7 | indent_size = 2 8 | 9 | [*.js] 10 | charset = utf-8 11 | indent_size = 4 12 | indent_style = tab 13 | 14 | [*.md] 15 | charset = utf-8 16 | indent_size = 4 17 | indent_style = space 18 | 19 | [package.json] 20 | charset = utf-8 21 | indent_size = 2 22 | indent_style = tab -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Example code to execute the bug** 14 | A short code snippet to reproduce the bug if possible. 15 | 16 | **Expected behaviour** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Error Logs** 20 | If applicable, add error logs to help explain your problem. Please use a code tag for that. 21 | 22 | **(please complete the following information):** 23 | - Version [e.g. v2.2.1] 24 | - Node Version [e.g. v10.0.3] 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: sterlingwes/gh-pages-deploy-action@v1.1 14 | with: 15 | access-token: ${{ secrets.ACCESS_TOKEN }} 16 | source-directory: docs 17 | build-command: yarn run docs 18 | -------------------------------------------------------------------------------- /.github/workflows/nodejs_test.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [10.x, 12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: yarn install and test 21 | run: | 22 | yarn install 23 | yarn run test 24 | env: 25 | CI: true 26 | -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: 12 15 | - run: npm install 16 | publish-npm: 17 | needs: build 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v1 21 | - uses: actions/setup-node@v1 22 | with: 23 | node-version: 12 24 | registry-url: https://registry.npmjs.org/ 25 | - run: npm publish 26 | env: 27 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 28 | 29 | publish-gpr: 30 | needs: build 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v1 34 | - uses: actions/setup-node@v1 35 | with: 36 | node-version: 12 37 | registry-url: https://npm.pkg.github.com/ 38 | scope: '@Mondanzo' 39 | - run: npm publish 40 | env: 41 | NODE_AUTH_TOKEN: ${{secrets.GH_PACKAGES}} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | docs/ 3 | 4 | yarn.lock 5 | package-lock.json 6 | *.log 7 | yarn-error.log -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file for the future. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | The dates will be documented in the European format (DD-MM-YYYY) 8 | 9 | ## [2.2.2] - 20-01-2021 10 | 11 | ### Changes 12 | 13 | TypeScript declaration fix by [Davoleo](https://github.com/Davoleo). Presumibly now should TypeScript be supported. 14 | 15 | ## [2.2.1] - 14-01-2021 16 | 17 | ### Changes 18 | 19 | Added type declarations for typescript. Thanks to [fractaal](https://github.com/fractaal) for writing them! 20 | 21 | ## [2.2.0] - 10-01-2021 22 | 23 | ### Changes 24 | 25 | **ModFile.getDependencies** and **ModFile.getDependenciesFiles** now contain another parameter called `categories` which defaults to an array of `[3]`. This parameter can be used to specify which type of dependencies to get. 26 | 27 | ### Added 28 | 29 | **CurseForgeAPI.DEPENDENCY_TYPE** is an enum containing a list of categories to use for getting dependencies. 30 | 31 | ## [2.1.1] - 27-12-2020 32 | 33 | ### Fixes 34 | 35 | **ModFile.getDependencies** now returns an empty array if no dependencies exist instead of running forever. 36 | 37 | **ModFile.getDependenciesFiles** now returns an empty array if no dependencies exist instead of running forever. 38 | 39 | ## [2.1.0] - 28-04-2020 40 | 41 | ### Changes 42 | 43 | **ModFile.download** now takes a new argument called `simulate` with which you can just try to download a file without creating a new file. If you're not using callbacks everything should work just fine. There's also `url` now which should always refer to `this.download_url` and `tries`. Tries is used to avoid redirects loop and cancels a download if more than 10 redirects are send. 44 | 45 | ## [2.0.1] - 24-04-2020 46 | 47 | ### Fixes 48 | 49 | **CurseForgeAPI.getModDescription()** now returns a html string instead of throwing a json error. 50 | 51 | ## [2.0.0] - 24-04-2020 52 | 53 | ### Added 54 | 55 | ## Global 56 | 57 | `options.searchFilter` can be used to search for keywords, can be an author, name or any string. 58 | 59 | ## CurseForgeAPI.SORT_TYPES 60 | 61 | an "enum" for the different types of sorting. Please check the docs for more information. 62 | 63 | ## CurseForgeAPI.getModDescription() 64 | 65 | Return the html description of a mod identified by id. 66 | 67 | ## Object::Mod 68 | 69 | `mod.popularityScore` is a float of some kind of score Cursforge added. 70 | 71 | `mod.gamePopularityRank` is a integer of some kind of rank Curseforge added. 72 | 73 | `mod.primaryLanguage` is a string containing the primary language the mod is written in. 74 | 75 | `mod.defaultFileId` is some kind id, probably the default advertised file. 76 | 77 | `mod.latestFiles` an Array of ModFiles of the latest released files. 78 | 79 | `mod.attachments` attachments from the description. The first item will always be the mod logo, therefore you can use `mod.logo`. Please note those are not strings and instead are objects. please log their structure to understand how to use them. 80 | 81 | `mod.featured` a boolean telling if the mod is featured. 82 | 83 | `mod.available` a boolean telling if the mod is available. 84 | 85 | `mod.experimental` a boolean telling if the mod is experimental. 86 | 87 | `mod.released` is a Timestamp of when the mod got released. 88 | 89 | `mod.authors` is an array of objects. Please log those objects to understand how to use them. 90 | 91 | **mod.getDescription()**: 92 | is a short-hand for `CurseforgeAPI.getDescription` 93 | 94 | ## Object::ModFile 95 | 96 | `modFile.available` a boolean telling if the mod file is available. 97 | 98 | ### Changed 99 | 100 | ## Global 101 | 102 | `identifier` must be an integer now, sadly. 103 | 104 | `options.mc_version` now is `options.gameVersion`. 105 | 106 | `options.page_size` now is `options.pageSize`. 107 | 108 | `options.page_num` now is `options.index`. 109 | 110 | ## Object::Mod 111 | 112 | `logo` is now an object of type "Attachment" (I couldn't get to make Attachment Objects classes yet). 113 | 114 | !!! `owner` is now an array of objects called `authors`!!! 115 | 116 | ## Object::ModFile 117 | 118 | **modFile.download**: 119 | `options` got removed and replaced with only `override` now. `auto_check` got removed. 120 | 121 | `mod_dependencies` is now an array of objects, it's suggested to use `modFile.getDependencies()` 122 | 123 | ### Removed 124 | 125 | ## Global 126 | 127 | `options.newest_only` got removed. 128 | 129 | `options.mod_name` got removed. Use `options.searchFilter` instead. 130 | 131 | `options.owner` got removed. Use `options.searchFilter` instead. 132 | 133 | ## Object::Mod 134 | 135 | `mod.owner` got removed! Please use `mod.authors` instead. 136 | 137 | `mod.categories` got removed. There isn't much knowledege yet about how the categories work now. 138 | 139 | `mod.description` got removed. Please see `CurseforgeAPI.getModDescription()` or `Mod.getDescription()` 140 | 141 | ## Object::ModFile 142 | 143 | `modFile.mod_id` got removed. 144 | 145 | `modFile.mod_key` got removed. 146 | 147 | `modFile.file_md5` got removed. 148 | 149 | `modFile.java_versions` got removed. 150 | 151 | **modFile.check_file()**: got removed. 152 | 153 | --- 154 | 155 | ### Final Words 156 | 157 | this is my first Changelog and as well first ever really published project so I'd love to recieve feedback and suggestion on how to improve! - Mondanzo 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mondanzo 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 | -------------------------------------------------------------------------------- /PROJECTS.md: -------------------------------------------------------------------------------- 1 | # Projects using this api 2 | 3 | - ...actually none so far but if you want to add your Project to this list just make a pull request with an updated list or add an issue with your project and I'll take care of adding it! 4 | -------------------------------------------------------------------------------- /conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [], 3 | "recurseDepth": 10, 4 | "source": { 5 | "include": [ 6 | "./index.js", 7 | "./readme.md", 8 | "./objects/Files.js", 9 | "./objects/Mod.js" 10 | ], 11 | "includePattern": ".+\\.js(doc|x)?$", 12 | "excludePattern": "(^|\\/|\\\\)_" 13 | }, 14 | "sourceType": "module", 15 | "tags": { 16 | "allowUnknownTags": true, 17 | "dictionaries": ["jsdoc", "closure"] 18 | }, 19 | "templates": { 20 | "systemName": "Curseforge API Wrapper", 21 | "systemSummary": "A node package for interacting with the Curseforge Minecraft Mods API.", 22 | "copyright": "©Mondanzo 2019", 23 | "includeDate": false 24 | }, 25 | "opts": { 26 | "template": "./node_modules/foodoc/template" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare class ModFile { 2 | /** 3 | * @name ModFile 4 | * @class ModFile 5 | * @description A File Object representing a file of a specific mod 6 | * @param {Object} file_object - File object to create object from 7 | * @property {string[]} minecraft_versions - The minecraft versions this mod file is compatible with. 8 | * @property {number} id - The id of the file object 9 | * @property {string} file_name - The name of the mod file it got stored with. 10 | * @property {string} file_size - The size of the mod file as string. (Yeah it's gross) 11 | * @property {string} release_type - the type of the mod file release. 12 | * @property {string} mod_key - The Curse slug of the mod the file belongs to. 13 | * @property {string} download_url - The url to the mod file to download. 14 | * @property {number} downloads - The amount of downloads of this mod file. 15 | * @property {timestamp} timestamp - A timestamp of the time the file got uploaded. 16 | * @property {string[]} mod_dependencies - A list of dependencies for this file. 17 | * @property {boolean} available - true if the file is available. 18 | */ 19 | minecraft_versions: string[]; 20 | id: number; 21 | file_name: string; 22 | file_size: string; 23 | release_type: string; 24 | mod_key: string; 25 | download_url: string; 26 | downloads: number; 27 | timestamp: Date; 28 | mod_dependencies: string[]; 29 | available: boolean; 30 | 31 | download(path: string, override: boolean, simulate: boolean, callback: Function, url: string, tries: number): Promise; 32 | getDependencies(callback?: Function, categories?: number[]): Promise; 33 | getDependenciesFiles(callback?: Function, categories?: number[]): Promise; 34 | } 35 | 36 | declare class Mod { 37 | /** 38 | * @name Mod 39 | * @class Mod 40 | * @description A Mod Object representing a file of a specific mod 41 | * @param {Object} mod_object - Mod object to create object from 42 | * @property {number} id - The Curse Id of the mod. 43 | * @property {string} name - The display name of the mod. 44 | * @property {object[]} authors - An array of authors names. 45 | * @property {object[]} attachments - An array of attachments objects from the description. 46 | * @property {string} url - The url to the mods page. 47 | * @property {string} summary - A short description to advertise the mod. 48 | * @property {number} defaultFileId - The default file id of the mod. 49 | * @property {number} downloads - The amount of downloads of the mod. 50 | * @property {ModFile[]} latestFiles - An array of ModFile's containing the latest files. 51 | * @property {string} key - The Curse slug of the mod. 52 | * @property {boolean} featured - Is the mod featured? 53 | * @property {number} popularityScore - Some kind of score? Not sure. 54 | * @property {number} gamePopularityRank - The rank of the mod. 55 | * @property {string} primaryLanguage - the primary language of the mod 56 | * @property {object} logo - The attachment object of the logo of the mod. 57 | * @property {timestamp} created - A timestamp of the time the mod got created. 58 | * @property {timestamp} updated - A timestamp of the time the mod got updated. 59 | * @property {timestamp} released - A timestamp of the time the mod got released. 60 | * @property {boolean} available - true if the mod is available. 61 | * @property {boolean} experimental - true if the mod is experimental. 62 | */ 63 | id: number; 64 | name: string; 65 | authors: Record[]; 66 | attachments: Record[]; 67 | url: string; 68 | summary: string; 69 | defaultFileId: number; 70 | downloads: number; 71 | latestFiles: ModFile[]; 72 | key: string; 73 | featured: boolean; 74 | popularityScore: number; 75 | gamePopularityRank: number; 76 | primaryLanguage: string; 77 | logo: Record; 78 | created: Date; 79 | updated: Date; 80 | released: Date; 81 | available: boolean; 82 | experimental: boolean; 83 | 84 | getFiles(callback?: Function): Promise; 85 | getDescription(callback?: Function): Promise; 86 | } 87 | 88 | declare module 'mc-curseforge-api' { 89 | export const SORT_TYPES: { 90 | FEATURED: 0, 91 | POPULARITY: 1, 92 | LAST_UPDATE: 2, 93 | NAME: 3, 94 | AUTHOR: 4, 95 | TOTAL_DOWNLOADS: 5 96 | } 97 | 98 | export const DEPENDENCY_TYPE: { 99 | EMBEDDED_LIBRARY: 1, 100 | OPTIONAL: 2, 101 | REQUIRED: 3, 102 | TOOL: 4, 103 | INCOMPATIBLE: 5, 104 | INCLUDE: 6 105 | } 106 | 107 | /** 108 | * @function getMods 109 | * 110 | * @description Get an overview of all possible mods. 111 | * @param {Object} options - A Object containing optional parameters. 112 | * @param {string} options.gameVersion - The Minecraft version string to use. ("1.12.2", "1.13") 113 | * @param {string} options.searchFilter - Term to search for. 114 | * @param {number} options.index - The page to use. 115 | * @param {number} options.pageSize - The amount of items to show per page. (Limit 500) 116 | * @param {number} options.sort - The method to sort with @see SORT_TYPES 117 | * @param {function} callback - Optional callback to use instead of Promise. 118 | * @returns {Promise.} A promise containing the json object returned by the Curse API on successful 200 response. 119 | */ 120 | export function getMods(options?: { 121 | gameVersion?: string; 122 | searchFilter?: string; 123 | index?: number; 124 | pageSize?: number;}, 125 | callback?: Function): Promise 126 | 127 | /** 128 | * @function getMod 129 | * 130 | * @description Get information about a specific mod using the identifier. 131 | * @param {number} identifier - The mods curse id to find the mod with. 132 | * @param {function} callback - Optional callback to use instead of Promise. 133 | * @returns {Promise.} A promise containing the json object returned by the Curse API on successful 200 response. 134 | */ 135 | export function getMod(identifier: number, callback?: Function): Promise; 136 | 137 | /** 138 | * @function getModFiles 139 | * 140 | * @description Get information about the releases of a specific mod. 141 | * @param {number} identifier - The mods curse id to find the mod with. 142 | * @param {function} callback - Optional callback to use instead of Promise. 143 | * @returns {Promise.} A promise containing the json object returned by the Curse API on successful 200 response. 144 | */ 145 | export function getModFiles(identifier: number, callback?: Function): Promise; 146 | 147 | /** 148 | * @function getModDescription 149 | * 150 | * @description Get the html string for the mod description. 151 | * @param {number} identifier - The mods curse id to find the mod with. 152 | * @param {function} callback - Optional callback to use instead of Promise. 153 | * @returns {Promise.} A promise containing the html string returned by the Curse API on successful 200 response. 154 | */ 155 | export function getModDescription(identifier: number, callback?: Function): Promise; 156 | 157 | } 158 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const https = require("https"); 2 | const querystring = require("querystring"); 3 | 4 | const Mod = require("./objects/Mod"); 5 | const ModFile = require("./objects/Files"); 6 | 7 | const base_url = "https://addons-ecs.forgesvc.net/api/v2/addon/"; 8 | 9 | /** 10 | * @description Generic Conversion Function (The Epic GCF) 11 | * @private 12 | * @param {Object} object - The object 13 | * @returns {Object} Returns the object 14 | */ 15 | function basic_conversion_function(object) { 16 | return object; 17 | } 18 | 19 | /** 20 | * @description Helper function to get content 21 | * @private 22 | * @param {string} url - Url to get 23 | * @param {Object} options - Object to stringify to url options 24 | */ 25 | function innerGet( 26 | url, 27 | options = {}, 28 | conversionFunction = basic_conversion_function, 29 | PARSE = true 30 | ) { 31 | return new Promise((resolve, reject) => { 32 | if (Object.keys(options).length) 33 | url += "&" + querystring.stringify(options); 34 | https.get(url, function (response) { 35 | if (response && response.statusCode === 200) { 36 | let data = ""; 37 | response.on("data", (chunk) => { 38 | data += chunk; 39 | }); 40 | 41 | response.on("end", () => { 42 | if (PARSE) resolve(conversionFunction(JSON.parse(data))); 43 | else resolve(conversionFunction(data)); 44 | }); 45 | } else { 46 | reject(response.statusCode); 47 | } 48 | }); 49 | }); 50 | } 51 | 52 | /** 53 | * @description the purpose of this package is to interact with the Curseforge API with simple functions instead of having to write all the requests yourself. 54 | * @module CurseForgeAPI 55 | * @see getMods 56 | */ 57 | 58 | /** 59 | * @description Helper Object for variations of sort options 60 | * @readonly 61 | * @enum {number} 62 | */ 63 | module.exports.SORT_TYPES = { 64 | FEATURED: 0, // Sort by Featured 65 | POPULARITY: 1, // Sort by Popularity 66 | LAST_UPDATE: 2, // Sort by Last Update 67 | NAME: 3, // Sort by Name 68 | AUTHOR: 4, // Sort by Author 69 | TOTAL_DOWNLOADS: 5, // Sort by Total Downloads 70 | }; 71 | 72 | /** 73 | * @description Helper Object for types of dependencies and incompatibilities. 74 | * @readonly 75 | * @enum {number} 76 | */ 77 | module.exports.DEPENDENCY_TYPE = { 78 | EMBEDDED_LIBRARY: 1, 79 | OPTIONAL: 2, 80 | REQUIRED: 3, 81 | TOOL: 4, 82 | INCOMPATIBLE: 5, 83 | INCLUDE: 6 84 | } 85 | 86 | /** 87 | * @function getMods 88 | * 89 | * @description Get an overview of all possible mods. 90 | * @param {Object} options - A Object containing optional parameters. 91 | * @param {string} options.gameVersion - The Minecraft version string to use. ("1.12.2", "1.13") 92 | * @param {string} options.searchFilter - Term to search for. 93 | * @param {number} options.index - The page to use. 94 | * @param {number} options.pageSize - The amount of items to show per page. (Limit 500) 95 | * @param {number} options.sort - The method to sort with @see SORT_TYPES 96 | * @param {function} callback - Optional callback to use instead of Promise. 97 | * @returns {Promise.} A promise containing the json object returned by the Curse API on successful 200 response. 98 | */ 99 | module.exports.getMods = function (options = {}, callback) { 100 | if (options && typeof options === "function") { 101 | callback = options; 102 | options = {}; 103 | } 104 | let promise = innerGet( 105 | base_url + "search?gameId=432§ionId=6", 106 | options, 107 | function (obj) { 108 | let mods = []; 109 | for (let m of Object.values(obj)) { 110 | mods.push(new Mod(m)); 111 | } 112 | return mods; 113 | } 114 | ); 115 | if (callback && typeof callback === "function") 116 | promise.then(callback.bind(null, null), callback); 117 | return promise; 118 | }; 119 | 120 | /** 121 | * @function getMod 122 | * 123 | * @description Get information about a specific mod using the identifier. 124 | * @param {number} identifier - The mods curse id to find the mod with. 125 | * @param {function} callback - Optional callback to use instead of Promise. 126 | * @returns {Promise.} A promise containing the json object returned by the Curse API on successful 200 response. 127 | */ 128 | module.exports.getMod = function (identifier, callback) { 129 | let promise = innerGet(base_url + identifier, {}, function (obj) { 130 | return new Mod(obj); 131 | }); 132 | if (callback && typeof callback === "function") 133 | promise.then(callback.bind(null, null), callback); 134 | return promise; 135 | }; 136 | 137 | /** 138 | * @function getModFiles 139 | * 140 | * @description Get information about the releases of a specific mod. 141 | * @param {number} identifier - The mods curse id to find the mod with. 142 | * @param {function} callback - Optional callback to use instead of Promise. 143 | * @returns {Promise.} A promise containing the json object returned by the Curse API on successful 200 response. 144 | */ 145 | module.exports.getModFiles = function (identifier, callback) { 146 | let promise = innerGet( 147 | base_url + identifier + "/files", 148 | undefined, 149 | function (obj) { 150 | let files = []; 151 | for (let f of Object.values(obj)) { 152 | files.push(new ModFile(f)); 153 | } 154 | return files; 155 | } 156 | ); 157 | if (callback && typeof callback === "function") 158 | promise.then(callback.bind(null, null), callback); 159 | return promise; 160 | }; 161 | 162 | /** 163 | * @function getModDescription 164 | * 165 | * @description Get the html string for the mod description. 166 | * @param {number} identifier - The mods curse id to find the mod with. 167 | * @param {function} callback - Optional callback to use instead of Promise. 168 | * @returns {Promise.} A promise containing the html string returned by the Curse API on successful 200 response. 169 | */ 170 | module.exports.getModDescription = function (identifier, callback) { 171 | let promise = innerGet( 172 | base_url + identifier + "/description", 173 | undefined, 174 | function (obj) { 175 | return obj; 176 | }, 177 | false 178 | ); 179 | if (callback && typeof callback === "function") 180 | promise.then(callback.bind(null, null), callback); 181 | return promise; 182 | }; 183 | -------------------------------------------------------------------------------- /objects/Files.js: -------------------------------------------------------------------------------- 1 | const curseforge = require("../index"); 2 | 3 | const fs = require("fs"); 4 | const ph = require("path"); 5 | const crypto = require("crypto"); 6 | const https = require("https"); 7 | 8 | module.exports = class { 9 | /** 10 | * @method ModFile.download 11 | * @description Download the file to a specific file 12 | * @param {string} path - absolute path to save the mod to. 13 | * @param {boolean} override - Should the file be overwritten if it already exists? Defaults to false. 14 | * @param {boolean} simulate - Doesn't download a file it just tries to find the proper website. Used for testing. 15 | * @param {function} callback - Optional callback to use as alternative to Promise. 16 | * @returns {Promise.} A Promise containing the selected absolute path for convenience. 17 | */ 18 | download( 19 | path, 20 | override = false, 21 | simulate = false, 22 | callback, 23 | url = this.download_url, 24 | tries = 10 25 | ) { 26 | if (override && typeof override === "function") { 27 | callback = override; 28 | override = false; 29 | } else if (override && typeof override === "object") { 30 | override = override.override; 31 | } 32 | 33 | let promise = new Promise((resolve, reject) => { 34 | if (tries < 1) reject("Download canceled after 10 redirects."); 35 | if (!ph.isAbsolute(path)) reject("Path is not absolute."); 36 | if (fs.existsSync(path)) { 37 | if (override) fs.unlinkSync(path); 38 | else reject("File exists and override is false"); 39 | } 40 | https.get(url, (response) => { 41 | if (response.statusCode >= 300 && response.statusCode < 400) { 42 | if (response.headers["location"]) 43 | resolve( 44 | this.download( 45 | path, 46 | override, 47 | simulate, 48 | callback, 49 | response.headers["location"], 50 | tries - 1 51 | ) 52 | ); 53 | return; 54 | } else if (response.statusCode !== 200) { 55 | reject("File couldn't be downloaded."); 56 | } 57 | if (simulate) { 58 | resolve(path); 59 | return; 60 | } else { 61 | response.pipe(fs.createWriteStream(path)); 62 | response.on("end", () => { 63 | resolve(path); 64 | }); 65 | } 66 | }); 67 | }); 68 | 69 | if (callback && typeof callback === "function") 70 | promise.then(callback.bind(null, null), callback); 71 | 72 | return promise; 73 | } 74 | 75 | /** 76 | * @private 77 | * @param {curseforge.getMod} method 78 | * @param {function} callback 79 | */ 80 | __please_dont_hate_me(method, callback, dependencies) { 81 | let promise = new Promise((resolve, reject) => { 82 | let mods = []; 83 | let amount = dependencies.length; 84 | if(amount <= 0){ 85 | resolve([]); 86 | } 87 | for (let dep of dependencies) { 88 | method(dep.addonId) 89 | .then((res) => { 90 | mods.push(res); 91 | if (--amount === 0) { 92 | resolve(mods); 93 | } 94 | }) 95 | .catch((err) => reject); 96 | } 97 | }); 98 | if (callback && typeof callback === "function") 99 | promise.then(callback.bind(null, null), callback); 100 | 101 | return promise; 102 | } 103 | 104 | /** 105 | * @method ModFile.getDependencies 106 | * @description Get all dependencies required by this mod. 107 | * @param {function} callback - Optional callback to use as alternative to Promise 108 | * @param {array} [categories=[1,3]] - Array of categories to get the Dependencies for. @see CurseForgeAPI.DEPENDENCY_TYPE 109 | * @returns {Promise.} Array of Mods who are marked as dependency or an empty array if no dependencies exist. 110 | */ 111 | getDependencies(callback, categories=[1, 3]) { 112 | if(typeof callback == "array") 113 | categories = callback; 114 | 115 | let dependenciesToLoad = this.mod_dependencies.filter(mod => { 116 | return categories.includes(mod.type); 117 | }) 118 | 119 | return this.__please_dont_hate_me(curseforge.getMod, callback, dependenciesToLoad); 120 | } 121 | 122 | /** 123 | * @method ModFile.getDependenciesFiles 124 | * @description Get all dependencies required by this mod. 125 | * @param {function} callback - Optional callback to use as alternative to Promise 126 | * @param {array} [categories=[3]] - Array of categories to get the Dependencies for. @see CurseForgeAPI.DEPENDENCY_TYPE 127 | * @returns {Promise.} Array of ModFiles who are marked as dependency or an empty array if no dependencies exist. 128 | */ 129 | getDependenciesFiles(callback, categories=[1, 3]) { 130 | if(typeof callback == "array") 131 | categories = callback; 132 | let dependenciesToLoad = this.mod_dependencies.filter(mod => { 133 | return categories.includes(mod.type); 134 | }) 135 | return this.__please_dont_hate_me(curseforge.getModFiles, callback, dependenciesToLoad); 136 | } 137 | 138 | /** 139 | * @name ModFile 140 | * @class ModFile 141 | * @description A File Object representing a file of a specific mod 142 | * @param {Object} file_object - File object to create object from 143 | * @property {string[]} minecraft_versions - The minecraft versions this mod file is compatible with. 144 | * @property {string} file_name - The name of the mod file it got stored with. 145 | * @property {string} file_size - The size of the mod file as string. (Yeah it's gross) 146 | * @property {string} release_type - the type of the mod file release. 147 | * @property {string} mod_key - The Curse slug of the mod the file belongs to. 148 | * @property {string} download_url - The url to the mod file to download. 149 | * @property {number} downloads - The amount of downloads of this mod file. 150 | * @property {timestamp} timestamp - A timestamp of the time the file got uploaded. 151 | * @property {string[]} mod_dependencies - A list of dependencies for this file. 152 | * @property {boolean} available - true if the file is available. 153 | */ 154 | constructor(file_object) { 155 | this.id = file_object.id; 156 | this.minecraft_versions = file_object.gameVersion; 157 | this.file_name = file_object.file_name; 158 | this.file_size = file_object.fileLength; 159 | this.timestamp = file_object.fileDate; 160 | this.release_type = file_object.releaseType; 161 | this.download_url = file_object.downloadUrl; 162 | this.downloads = file_object.download_count; 163 | this.mod_dependencies = file_object.dependencies || []; 164 | this.alternate = file_object.isAlternate; 165 | this.alternate_id = file_object.alternateFileId; 166 | this.available = file_object.isAvailable; 167 | } 168 | }; 169 | -------------------------------------------------------------------------------- /objects/Mod.js: -------------------------------------------------------------------------------- 1 | const curseforge = require("../index"); 2 | const ModFile = require("../objects/Files"); 3 | 4 | module.exports = class { 5 | /** 6 | * @method Mod.getFiles 7 | * @description Simple function to call GetModFiles with predefined identifier. 8 | * @see CurseForgeAPI.getModFiles 9 | */ 10 | getFiles(options, callback) { 11 | return curseforge.getModFiles(this.id, options, callback); 12 | } 13 | 14 | /** 15 | * @method Mod.getDescription 16 | * @description Simple function to call GetModDescription with predefined identifier. 17 | * @see CurseForgeAPI.getModDescription 18 | */ 19 | getDescription(callback) { 20 | return curseforge.getModDescription(this.id, callback); 21 | } 22 | 23 | /** 24 | * @name Mod 25 | * @class Mod 26 | * @description A Mod Object representing a file of a specific mod 27 | * @param {Object} mod_object - Mod object to create object from 28 | * @property {number} id - The Curse Id of the mod. 29 | * @property {string} name - The display name of the mod. 30 | * @property {object[]} authors - An array of authors names. 31 | * @property {object[]} attachments - An array of attachments objects from the description. 32 | * @property {string} url - The url to the mods page. 33 | * @property {string} summary - A short description to advertise the mod. 34 | * @property {number} defaultFileId - The default file id of the mod. 35 | * @property {number} downloads - The amount of downloads of the mod. 36 | * @property {ModFile[]} latestFiles - An array of ModFile's containing the latest files. 37 | * @property {string} key - The Curse slug of the mod. 38 | * @property {boolean} featured - Is the mod featured? 39 | * @property {number} popularityScore - Some kind of score? Not sure. 40 | * @property {number} gamePopularityRank - The rank of the mod. 41 | * @property {string} primaryLanguage - the primary language of the mod 42 | * @property {object} logo - The attachment object of the logo of the mod. 43 | * @property {timestamp} created - A timestamp of the time the mod got created. 44 | * @property {timestamp} updated - A timestamp of the time the mod got updated. 45 | * @property {timestamp} released - A timestamp of the time the mod got released. 46 | * @property {boolean} available - true if the mod is available. 47 | * @property {boolean} experimental - true if the mod is experimental. 48 | */ 49 | constructor(mod_object) { 50 | this.id = mod_object.id; 51 | this.name = mod_object.name; 52 | this.authors = mod_object.authors; 53 | this.attachments = mod_object.attachments; 54 | this.url = mod_object.websiteUrl; 55 | this.summary = mod_object.summary; 56 | this.defaultFileId = mod_object.defaultFileId; 57 | this.downloads = mod_object.downloadCount; 58 | this.latestFiles = []; 59 | for (let f of mod_object.latestFiles) { 60 | this.latestFiles.push(new ModFile(f)); 61 | } 62 | this.key = mod_object.slug; 63 | this.featured = mod_object.isFeatured; 64 | this.popularityScore = mod_object.popularityScore; 65 | this.gamePopularityRank = mod_object.gamePopularityRank; 66 | this.primaryLanguage = mod_object.primaryLanguage; 67 | this.logo = this.attachments[0]; 68 | this.updated = mod_object.dateModified; 69 | this.created = mod_object.dateCreated; 70 | this.released = mod_object.dateReleased; 71 | this.available = mod_object.isAvailable; 72 | this.experimental = mod_object.isExperimental; 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mc-curseforge-api", 3 | "version": "2.2.3", 4 | "description": "API to interact with the minecraft mods curseforge api", 5 | "main": "index.js", 6 | "author": "Mondanzo", 7 | "homepage": "https://mondanzo.github.io/mc-curseforge-api/index.html", 8 | "keywords": [ 9 | "Minecraft", 10 | "Curse", 11 | "Mods", 12 | "Modding" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/Mondanzo/mc-curseforge-api.git" 17 | }, 18 | "license": "MIT", 19 | "scripts": { 20 | "docs": "jsdoc -c conf.json -d docs", 21 | "test": "mocha" 22 | }, 23 | "private": false, 24 | "devDependencies": { 25 | "foodoc": "^0.0.9", 26 | "jsdoc": "^3.6.3", 27 | "mocha": "^6.2.2" 28 | }, 29 | "dependencies": {} 30 | } 31 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # this package is deprecated. Please stop using this and instead use [Mondanzo/node-curseforge](https://github.com/Mondanzo/node-curseforge) 2 | 3 | # please read 4 | Due to Overwolf taking over Curseforge. Bunch of parts from the api either won't work, don't show all mods or even outdated info for a mod. For now I highly recommend to use a different library, or use modrinth over Curseforge, (we all love us an Open-Source mod page instead of closed source). I'm not sure if I will continue working on this project or if it will be archived forever. 5 | 6 | 7 | # mc-curseforge-api [![Codacy Badge](https://api.codacy.com/project/badge/Grade/229792d8c6484b99b47313081248b2fd)](https://www.codacy.com/manual/Mondanzo/mcCurseforgeAPI?utm_source=github.com&utm_medium=referral&utm_content=Mondanzo/mcCurseforgeAPI&utm_campaign=Badge_Grade) [![npm bundle size](https://img.shields.io/bundlephobia/min/mc-curseforge-api) ![npm (tag)](https://img.shields.io/npm/v/mc-curseforge-api/latest)](https://www.npmjs.com/package/mc-curseforge-api) 8 | 9 | Yeah a terrible package name but whatever. 10 | 11 | This package should make dealing with the Minecraft Mods Curseforge API a lot easier. 12 | Just quickly require it and you're good to go! 13 | 14 | ## Installation 15 | 16 | **Using yarn (which is obviously better)**\ 17 | `yarn add mc-curseforge-api` 18 | 19 | **Using npm**\ 20 | `npm install mc-curseforge-api --save` 21 | 22 | ## Examples 23 | 24 | **Get a list of mods:** 25 | 26 | ```javascript 27 | const curseforge = require("mc-curseforge-api"); 28 | 29 | curseforge.getMods().then((mods) => { 30 | console.log(mods); 31 | }); 32 | ``` 33 | 34 | **Search for mods by a string:** 35 | 36 | ```javascript 37 | curseforge.getMods({ searchFilter: "Vazkii_" }).then((mods) => { 38 | console.log(mods); 39 | }); 40 | ``` 41 | 42 | **Get a list of mods for a specific minecraft version:** 43 | 44 | ```javascript 45 | curseforge.getMods({ gameVersion: "1.12.2" }).then((mods) => { 46 | console.log(mods); 47 | }); 48 | ``` 49 | 50 | **Use paging for getting mods:** 51 | 52 | ```javascript 53 | curseforge.getMods({ index: 3, pageSize: 5 }).then((mods) => { 54 | console.log(mods); 55 | }); 56 | ``` 57 | 58 | See [curseforge.getMods](https://mondanzo.github.io/mc-curseforge-api/module-CurseForgeAPI.html#~getMods) for more options. 59 | 60 | **Download the mod file for a mod:** 61 | 62 | ```javascript 63 | mod.getFiles().then((files) => { 64 | files[0].download("./Mod.jar"); 65 | }); 66 | ``` 67 | 68 | **Get mod files for a specific minecraft version:** 69 | 70 | ```javascript 71 | curseforge.getModFiles(225643).then((files) => { 72 | console.log(files); 73 | }); 74 | ``` 75 | 76 | See [curseforge.getModFiles](https://mondanzo.github.io/mc-curseforge-api/module-CurseForgeAPI.html#~getModFiles) for more options. 77 | 78 | ## Documentation 79 | 80 | See the docs for more information [here](https://mondanzo.github.io/mc-curseforge-api/).\ 81 | (Those got made with JSDoc and I have no clue how to make them look better.) 82 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const curseforge = require("../index"); 2 | const assert = require("assert"); 3 | 4 | describe("getMods", function () { 5 | it("should return list of mods with no filtering containing only 25 items", async function () { 6 | this.timeout(10000); 7 | 8 | let mods = await curseforge.getMods({ pageSize: 25 }); 9 | assert.ok(mods.length == 25); 10 | let mod = mods.pop(); 11 | assert.ok(mod.hasOwnProperty("id")); 12 | assert.ok(mod.hasOwnProperty("key")); 13 | assert.ok(mod.hasOwnProperty("name")); 14 | assert.ok(mod.hasOwnProperty("summary")); 15 | assert.ok(mod.hasOwnProperty("attachments")); 16 | assert.ok(mod.hasOwnProperty("defaultFileId")); 17 | assert.ok(mod.hasOwnProperty("featured")); 18 | assert.ok(mod.hasOwnProperty("primaryLanguage")); 19 | assert.ok(mod.hasOwnProperty("gamePopularityRank")); 20 | assert.ok(mod.hasOwnProperty("popularityScore")); 21 | assert.ok(mod.hasOwnProperty("latestFiles")); 22 | assert.ok(mod.hasOwnProperty("url")); 23 | assert.ok(mod.hasOwnProperty("authors")); 24 | assert.ok(mod.hasOwnProperty("logo")); 25 | assert.ok(mod.hasOwnProperty("downloads")); 26 | assert.ok(mod.hasOwnProperty("created")); 27 | assert.ok(mod.hasOwnProperty("updated")); 28 | assert.ok(mod.hasOwnProperty("released")); 29 | assert.ok(mod.hasOwnProperty("available")); 30 | assert.ok(mod.hasOwnProperty("experimental")); 31 | }); 32 | 33 | it("should have a complete mod object returned from getMod after finding mod using key gathered from getMods", async function () { 34 | this.timeout(10000); 35 | 36 | let mod = await curseforge.getMod( 37 | (await curseforge.getMods({ pageSize: 1 })).pop().id 38 | ); 39 | assert.ok(mod.hasOwnProperty("id")); 40 | assert.ok(mod.hasOwnProperty("key")); 41 | assert.ok(mod.hasOwnProperty("name")); 42 | assert.ok(mod.hasOwnProperty("summary")); 43 | assert.ok(mod.hasOwnProperty("attachments")); 44 | assert.ok(mod.hasOwnProperty("defaultFileId")); 45 | assert.ok(mod.hasOwnProperty("featured")); 46 | assert.ok(mod.hasOwnProperty("primaryLanguage")); 47 | assert.ok(mod.hasOwnProperty("gamePopularityRank")); 48 | assert.ok(mod.hasOwnProperty("popularityScore")); 49 | assert.ok(mod.hasOwnProperty("latestFiles")); 50 | assert.ok(mod.hasOwnProperty("url")); 51 | assert.ok(mod.hasOwnProperty("authors")); 52 | assert.ok(mod.hasOwnProperty("logo")); 53 | assert.ok(mod.hasOwnProperty("downloads")); 54 | assert.ok(mod.hasOwnProperty("created")); 55 | assert.ok(mod.hasOwnProperty("updated")); 56 | assert.ok(mod.hasOwnProperty("released")); 57 | assert.ok(mod.hasOwnProperty("available")); 58 | assert.ok(mod.hasOwnProperty("experimental")); 59 | }); 60 | 61 | it("should return a list of dependencies for Tinkers Construct only containing mantle", async function () { 62 | this.timeout(10000); 63 | let file = (await curseforge.getModFiles("74072")).pop(); 64 | let deps = await file.getDependencies(); 65 | assert.ok(deps.length > 0); 66 | assert.ok(deps.pop().key === "mantle"); 67 | }); 68 | 69 | it("should return a list of dependencies files for Tinkers Construct only containing mantle", async function () { 70 | this.timeout(10000); 71 | let file = (await curseforge.getModFiles("74072")).pop(); 72 | let deps = await file.getDependenciesFiles(); 73 | assert.ok(deps.length > 0); 74 | }); 75 | 76 | it("should return a html string", async function () { 77 | this.timeout(10000); 78 | let description = await curseforge.getModDescription("74072"); 79 | assert.ok(typeof description === "string"); 80 | assert.ok(description[0] === "<"); 81 | }); 82 | 83 | it("Should simulate a download", async function () { 84 | this.timeout(10000); 85 | let file = (await curseforge.getModFiles("74072")).pop(); 86 | let p = await file.download("/file.jar", false, true); 87 | assert.ok(p === "/file.jar"); 88 | }); 89 | }); 90 | --------------------------------------------------------------------------------