{"id":42051946,"url":"https://github.com/sans3108/ddnet","last_synced_at":"2026-04-04T15:02:33.956Z","repository":{"id":44678302,"uuid":"512357244","full_name":"Sans3108/DDNet","owner":"Sans3108","description":"A typescript npm package for interacting with data from ddnet.org","archived":false,"fork":false,"pushed_at":"2025-11-03T16:42:30.000Z","size":2527,"stargazers_count":8,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-22T09:56:02.152Z","etag":null,"topics":["ddnet","ddnetwork","ddrace","ddracenetwork","network","teedata","teeworlds"],"latest_commit_sha":null,"homepage":"http://ddnet.js.org/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sans3108.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"Sans3108","ko_fi":"sansy"}},"created_at":"2022-07-10T05:56:05.000Z","updated_at":"2025-09-28T14:12:13.000Z","dependencies_parsed_at":"2024-11-04T22:27:37.988Z","dependency_job_id":"9dba1566-f467-4d2e-9e63-8c773568f448","html_url":"https://github.com/Sans3108/DDNet","commit_stats":{"total_commits":29,"total_committers":2,"mean_commits":14.5,"dds":"0.13793103448275867","last_synced_commit":"1be9953e9c3a9cab534176b6e104049274dffb33"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/Sans3108/DDNet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sans3108%2FDDNet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sans3108%2FDDNet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sans3108%2FDDNet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sans3108%2FDDNet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sans3108","download_url":"https://codeload.github.com/Sans3108/DDNet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sans3108%2FDDNet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28769212,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T06:37:25.426Z","status":"ssl_error","status_checked_at":"2026-01-26T06:37:23.039Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddnet","ddnetwork","ddrace","ddracenetwork","network","teedata","teeworlds"],"created_at":"2026-01-26T07:01:11.795Z","updated_at":"2026-04-04T15:02:33.921Z","avatar_url":"https://github.com/Sans3108.png","language":"TypeScript","funding_links":["https://github.com/sponsors/Sans3108","https://ko-fi.com/sansy"],"categories":[],"sub_categories":[],"readme":"# DDNet Readme\n\n[![NPM Version](https://img.shields.io/npm/v/ddnet?logo=npm)](https://www.npmjs.com/package/ddnet) [![NPM Downloads](https://img.shields.io/npm/dm/ddnet?logo=npm\u0026label=Downloads)](https://www.npmjs.com/package/ddnet) [![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/Sans3108/DDNet?logo=github\u0026label=Issues)](https://github.com/Sans3108/DDNet/issues)\n\nA typescript npm package for interacting with data from ddnet.org\n\n## Why?\n\nI was bored, and also I needed a decent package to use for interacting with ddnet.org programatically. It is also my first package and I tried my best to make it as good as possible.\n\n### Notice\n\nThis package heavily relies on the type checker for a lot of things, I cannot guarantee safe usage with plain/vanilla JavaScript!\n\n## Installation\n\nUsing your node package manager of choice, for example `pnpm`:\n\n```\n$ pnpm add ddnet\n```\n\n## Example Usage\n\n### Player class examples\n\nImport the `Player` class from the package and create a new instance of it.\n\n```ts\nimport { Player } from 'ddnet';\n\nconst me = await Player.new('Sans3108');\n\nconsole.log(me);\n\n/*\nPlayer {\n  name: 'Sans3108',\n  url: 'https://ddnet.org/players/Sans3108',\n  globalLeaderboard: GlobalLeaderboard {\n    completionist: { placement: 7023, points: 5182 },\n    team: null,\n    rank: null,\n    completionistLastMonth: { placement: 5516, points: 138 },\n    completionistLastWeek: { placement: 5202, points: 35 }\n  },\n  totalCompletionistPoints: 33375,\n  favoriteServer: 'GER',\n  finishes: Finishes {\n    first: Finish {\n      timestamp: 1628418102000,\n      mapName: 'Multeasymap',\n      timeSeconds: 2484.68,\n      timeString: '41:24⁶⁷',\n      rank: [Object],\n      region: 'UNK',\n      players: [Array]\n    },\n    recent: [\n      [RecentFinish],\n      ...\n    ]\n  },\n  ... (lots of other data)\n}\n*/\n```\n\nYou probably wouldn't want to log everything, just what you need. Examples below show how to log out the points of the player, the first finished map's name and the map with fastest finish time out of all the novice maps:\n\n```js\n// Points\nimport { Player } from 'ddnet';\nconst player = await Player.new('Sans3108');\n\nconsole.log(player.globalLeaderboard.completionist?.points); // 5182\n```\n\n```js\n// First finish\nimport { Player } from 'ddnet';\nconst player = await Player.new('Sans3108');\n\nconsole.log(player.finishes.first.mapName); // \"Multeasymap\"\n```\n\n```js\n// Fastest finish time of all novice maps\nimport { Player, isCompletedMapStats } from 'ddnet';\nconst player = await Player.new('Sans3108');\n\nconst completed = player.serverTypes.Novice.maps.filter(isCompletedMapStats);\nconst fastestTime = completed.sort((a, b) =\u003e a.bestTimeSeconds - b.bestTimeSeconds)[0];\n\nconsole.log(`${fastestTime.mapName} ${fastestTime.bestTimeString}`); // \"Tangerine 00:42⁸⁶\"\n```\n\n### Map class examples\n\nLet's say you're not interested in player data that much, and you want to check out on some maps, to do that:\n\n```ts\nimport { Map } from 'ddnet';\nconst map = await Map.new('Kobra 4');\n\nconsole.log(map);\n/*\nMap {\n  name: 'Kobra 4',\n  url: 'https://ddnet.org/maps/Kobra-32-4',\n  thumbnailUrl: 'https://ddnet.org/ranks/maps/Kobra_4.png',\n  webPreviewUrl: 'https://ddnet.org/mappreview/?map=Kobra+4',\n  type: 'Novice',\n  points: 4,\n  difficulty: 4,\n  mappers: [\n    Author {\n      name: 'Zerodin',\n      mapShowcaseUrl: 'https://ddnet.org/mappers/Zerodin',\n      playerUrl: 'https://ddnet.org/players/Zerodin'\n    }\n  ],\n  releasedTimestamp: 1438531140000,\n  ... (lots of other data)\n}\n*/\n```\n\nLike before, this may be a bit much, so let's see some examples. Here are some showcasing how to get a map's author, the number of points it awards upon completion, and the current time record:\n\n```ts\n// Author\n\nimport { Map } from 'ddnet';\n\nconst map = await Map.new('Grandma');\nconsole.log(map.mappers[0].name); // \"Fňokurka oo7\"\n\n// If the map has multiple authors, then:\nconst map = await Map.new('Nagi');\nconsole.log(map.mappers.map(a =\u003e a.name).join(' \u0026 ')); // \"Cøke \u0026 Arrow\"\n```\n\n```ts\n// Points\nimport { Map } from 'ddnet';\n\nconst map = await Map.new('EasyRight');\n\nconsole.log(map.points); // 4\n```\n\n```ts\n// Current record\nimport { Map } from 'ddnet';\n\nconst map = await Map.new('Baby Aim 1.0');\n\nconsole.log(`${map.finishes[0].players[0].name} ${map.finishes[0].timeString}`); // \"Cireme 06:25\"\n```\n\n### Skin rendering\n\nThe lib also provides a way to render still images of any skin, in any way (without animated feet)\n\n#### 0.6 (DDNet) skins\n\nWith inspiration from [TeeAssembler-2.0](https://github.com/AlexIsTheGuy/TeeAssembler-2.0) by [AlexIsTheGuy](https://github.com/AlexIsTheGuy) and help from [Patiga](https://github.com/Patiga)'s [Tee Rendering documentation](https://github.com/heinrich5991/libtw2/blob/master/doc/tee_rendering.md). It is able to render any 0.6 skin from either a skin name or buffer.\n\nExample usage:\n\n```ts\nimport { TeeSkin6 } from 'ddnet';\n\nconst mySkin = new TeeSkin6({ skinResource: '10Fox' });\nconst rendered = await mySkin.render(); // Do something with the rendered skin buffer\n\n// Or optionally, save it to a file by providing a file path\nawait mySkin.render({ saveFilePath: 'my-skin.png' }); // Still returns a buffer\n```\n\n_my-skin.png_\n\n![Skin Render Output](https://raw.githubusercontent.com/Sans3108/DDNet/master/misc/my-skin.png)\n\nYou can also customize the emote, and direction the tee is looking towards:\n\n```ts\nimport { TeeSkin6, TeeSkinEyeVariant } from 'ddnet';\n\nconst mySkin = new TeeSkin6({ skinResource: '10Fox' });\n\nawait mySkin.render({\n  saveFilePath: 'my-skin-happy-left.png',\n  eyeVariant: TeeSkinEyeVariant.happy,\n  viewAngle: 180 // left, since 0 is right\n});\n```\n\n_my-skin-happy-left.png_\n\n![Skin Render Output](https://raw.githubusercontent.com/Sans3108/DDNet/master/misc/my-skin-happy-left.png)\n\n#### 0.7 (Teeworlds) skins\n\nVery similar to 0.6 skins, the only difference being how the instance is initialized.\n\nExample usage:\n\n```ts\nimport { TeeSkin7, Color } from 'ddnet';\n\nconst skin = new TeeSkin7({\n  body: 'fox',\n  marking: 'fox',\n  eyes: 'negative'\n});\n\nconst rendered = await skin.render({\n  customColors: {\n    bodyTWcode: 1102443,\n    markingTWcode: -485425166,\n    feetTWcode: 1102450,\n    eyesTWcode: 1441632\n  }\n}); // Do something with the rendered skin buffer\n\n// Or optionally, save it to a file by providing a file path\n// Tip: You can import and use the Color class to easily specify colors instead of pasting numbers\nawait skin.render({\n  customColors: {\n    bodyTWcode: Color.from('#d85407').to('tw'),\n    markingTWcode: Color.from('#ffae7098').to('tw'),\n    feetTWcode: Color.from('#723920').to('tw'),\n    eyesTWcode: Color.from('#161414').to('tw')\n  },\n  saveFilePath: 'fox.png'\n}); // Still returns a buffer\n```\n\n_fox.png_\n\n![Skin Render Output](https://raw.githubusercontent.com/Sans3108/DDNet/master/misc/fox.png)\n\nOther customizations remain the same between 0.6 and 0.7 skins such as the emote, eye direction etc.\n\n## Cache\n\nMost classes cache results, this is done using the [keyv](https://www.npmjs.com/package/keyv) and [@keyv/sqlite](https://www.npmjs.com/package/@keyv/sqlite) packages. Cache data is stored in the `ddnet_cache.sqlite` file.\n\nIt's not really wise to mess with the cache, but there's a couple things you can do, mainly changing the TTL (Time-To-Live) of the cached items[\\*]() the class is responsible for, and clearing different cache pools.\n\n\u003csub\u003e\\* _Applies to newly added items_\u003c/sub\u003e\n\n## Building\n\nFor building this package yourself you will need the latest LTS version of Node.JS, and some knowledge of typescript. Package manager choice should not matter, but for smooth operations I recommend `pnpm`.\n\nFirst, clone the repository and navigate to it:\n\n```\n$ git clone https://github.com/Sans3108/DDNet.git\n$ cd DDNet\n```\n\nInstall the dependencies with:\n\n```\n$ pnpm install\n```\n\nAfter that, you're ready to open up the project in your code editor of choice, or, if you don't wish to change anything, simply run the following command to build the project:\n\n```\n$ pnpm build\n```\n\nAdditionaly you may re-build the `typedoc` documentation website with:\n\n```\n$ pnpm typedoc\n```\n\nAnd after that everything in the `/docs` directory should be up to date with your changes.\n\n## Contributions \u0026 Notes\n\nHelp is always appreciated, if you are able to contribute and have the know-how, please do! I will look over every PR and potentially we can integrate your changes!\n\nThis readme may not showcase everything, but that's why the [documentation website](https://ddnet.js.org) exists! Please check it out, explore and find what you need there if it wasn't shown here.\n\nIf something is missing or you would like to suggest something please [submit an issue](https://github.com/Sans3108/DDNet/issues/new) about it!\n\nIf you've made it this far and you consider this package useful, please consider starring [this repository](https://github.com/Sans3108/DDNet) so more people can see it!\n\n[![GitHub Repo stars](https://img.shields.io/github/stars/Sans3108/DDNet)](https://github.com/Sans3108/DDNet)\n\nAny bugs can be reported [here](https://github.com/Sans3108/DDNet/issues) or on discord by adding me: `Sans#0001`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsans3108%2Fddnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsans3108%2Fddnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsans3108%2Fddnet/lists"}