├── .gitignore ├── README.md ├── index.js ├── letters.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitWrite 2 | Write something on your contribution graph! 3 | 4 | ## What is GitWrite? 5 | GitWrite is a little tool I developed that lets you add a short message to a year on your contribution graph. GitHub lets you spoof commit dates, and while there are already tools out there to fake your graph, none of them let you write text. 6 | 7 | ## How do I use GitWrite? 8 | Install the global CLI! 9 | ```sh 10 | npm install -g gitwrite-cli 11 | ``` 12 | From there, run the `gitwrite` command in a new repository. Once you have a new repository, run `gitwrite "" `. The message must be in quotes. The username should be your GitHub profile's username, and if not provided, GitWrite will get a username from your Git global config. GitWrite will ask you for confirmation, and then make however many commits you need to add the text to your graph. If you ever want to remove it, just delete the GitHub repo. 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { exec } = require('child_process'); 4 | const fetch = require('node-fetch'); 5 | const prompt = require('prompt-sync')(); 6 | const type = require('./letters.js'); 7 | const run = (...args) => new Promise((resolve, reject) => exec(...args, (err, stdout, stderr) => err ? (() => { 8 | reject(err); 9 | console.log(stdout, stderr); 10 | })() : resolve(stdout))); 11 | 12 | process.argv.shift(); 13 | if (isNaN(process.argv[0])) process.argv.shift(); 14 | const args = process.argv; 15 | 16 | const [year, message, username] = args.map(v => isNaN(v) ? v : +v); 17 | 18 | (async () => { 19 | if (!year || !message) return console.log('Welcome to GitWrite! GitWrite lets you add a message to your GitHub contribution graph. To begin, make a new GitHub repo, clone it locally, and get ready to add commits!\n\nRun this command again with this syntax: gitwrite .\nMessage should be in qotes. Messages can only have alphanumeric characters, spaces. and some symbols.'); 20 | 21 | const gitUsername = (await run('git config user.name')).trim(); 22 | const data = await fetch('https://api.github.com/users/' + (username ? username : gitUsername)).then(r => r.json()); 23 | if (data.message) return console.log('Invalid GitHub username "' + gitUsername + '"'); 24 | 25 | const commands = type(year, message); 26 | 27 | console.log(`Welcome, ${username ? username: gitUsername}!`); 28 | console.log(`Using git repo in directory ${process.cwd()},`); 29 | console.log(`Staging ${commands.length / 6} commits to write ${message} in ${year}...`); 30 | prompt('Press enter to continue...'); 31 | 32 | for (let i = 0; i < commands.length; i++) { 33 | const command = commands[i]; 34 | await run(command); 35 | console.log(`${i + 1}/${commands.length} (${Math.round((i + 1) / commands.length * 1000) / 10}%)`); 36 | } 37 | 38 | console.log('Pushing changes...'); 39 | await run('git push'); 40 | console.log('Changes pushed!'); 41 | })(); 42 | -------------------------------------------------------------------------------- /letters.js: -------------------------------------------------------------------------------- 1 | const a = 2 | ` 3 | ## 4 | # # 5 | #### 6 | # # 7 | # # 8 | ` 9 | 10 | const b = 11 | ` 12 | ### 13 | # # 14 | ### 15 | # # 16 | ### 17 | ` 18 | 19 | const c = 20 | ` 21 | ## 22 | # 23 | # 24 | # 25 | ## 26 | ` 27 | 28 | const d = 29 | ` 30 | ### 31 | # # 32 | # # 33 | # # 34 | ### 35 | ` 36 | 37 | const e = 38 | ` 39 | ### 40 | # 41 | ### 42 | # 43 | ### 44 | ` 45 | 46 | const f = 47 | ` 48 | ### 49 | # 50 | ### 51 | # 52 | # 53 | ` 54 | 55 | const g = 56 | ` 57 | ## 58 | # 59 | # # 60 | # # 61 | ## 62 | ` 63 | 64 | const h = 65 | ` 66 | # # 67 | # # 68 | #### 69 | # # 70 | # # 71 | ` 72 | 73 | const i = 74 | ` 75 | ### 76 | # 77 | # 78 | # 79 | ### 80 | ` 81 | 82 | const j = 83 | ` 84 | #### 85 | # 86 | # 87 | # # 88 | # 89 | ` 90 | 91 | const k = 92 | ` 93 | # # 94 | # # 95 | ## 96 | # # 97 | # # 98 | ` 99 | 100 | const l = 101 | ` 102 | # 103 | # 104 | # 105 | # 106 | ### 107 | ` 108 | 109 | const m = 110 | ` 111 | # # 112 | ## ## 113 | # # # 114 | # # 115 | # # 116 | ` 117 | 118 | const n = 119 | ` 120 | # # 121 | ## # 122 | # ## 123 | # # 124 | # # 125 | ` 126 | 127 | const o = 128 | ` 129 | ## 130 | # # 131 | # # 132 | # # 133 | ## 134 | ` 135 | 136 | const p = 137 | ` 138 | ## 139 | # # 140 | ## 141 | # 142 | # 143 | ` 144 | 145 | const q = 146 | ` 147 | ### 148 | # # 149 | # # 150 | # # 151 | ## 152 | #` 153 | 154 | const r = 155 | ` 156 | ## 157 | # # 158 | ## 159 | # # 160 | # # 161 | ` 162 | 163 | const s = 164 | ` 165 | ### 166 | # 167 | ## 168 | # 169 | ### 170 | ` 171 | 172 | const t = 173 | ` 174 | ### 175 | # 176 | # 177 | # 178 | # 179 | ` 180 | 181 | const u = 182 | ` 183 | # # 184 | # # 185 | # # 186 | # # 187 | ## 188 | ` 189 | 190 | const v = 191 | ` 192 | # # 193 | # # 194 | # # 195 | # # 196 | # 197 | ` 198 | 199 | const w = 200 | ` 201 | # # 202 | # # 203 | # # # 204 | # # # 205 | # # 206 | ` 207 | 208 | const x = 209 | ` 210 | # # 211 | # # 212 | # 213 | # # 214 | # # 215 | ` 216 | 217 | const y = 218 | ` 219 | # # 220 | # # 221 | ### 222 | # 223 | ### 224 | ` 225 | 226 | const z = 227 | ` 228 | #### 229 | # 230 | # 231 | # 232 | #### 233 | ` 234 | 235 | const exclaimation = 236 | ` 237 | # 238 | # 239 | # 240 | 241 | # 242 | ` 243 | 244 | const at = 245 | ` 246 | ### 247 | # # # 248 | ##### 249 | # # 250 | # 251 | ###` 252 | 253 | const hashtag = 254 | ` 255 | # # 256 | ##### 257 | # # 258 | ##### 259 | # # 260 | ` 261 | 262 | const dollar = 263 | ` # 264 | #### 265 | # # 266 | ### 267 | # # 268 | #### 269 | # 270 | ` 271 | 272 | const percent = 273 | ` 274 | # # 275 | # 276 | # 277 | # 278 | # # 279 | ` 280 | 281 | const caret = 282 | ` 283 | # 284 | # # 285 | 286 | 287 | 288 | ` 289 | 290 | const ampersand = 291 | ` 292 | # 293 | # # 294 | # 295 | # # 296 | ## # 297 | ` 298 | 299 | const asterisk = 300 | ` 301 | # 302 | ### 303 | # 304 | 305 | 306 | ` 307 | 308 | const slash = 309 | ` 310 | # 311 | # 312 | # 313 | # 314 | # 315 | ` 316 | 317 | const plus = 318 | ` 319 | 320 | # 321 | ### 322 | # 323 | 324 | ` 325 | 326 | const minus = 327 | ` 328 | 329 | 330 | ### 331 | 332 | 333 | ` 334 | 335 | const equals = 336 | ` 337 | 338 | ### 339 | 340 | ### 341 | 342 | ` 343 | 344 | const underscore = 345 | ` 346 | 347 | 348 | 349 | 350 | 351 | ####` 352 | 353 | const space = 354 | ` 355 | 356 | 357 | 358 | 359 | 360 | ` 361 | 362 | const period = 363 | ` 364 | 365 | 366 | 367 | 368 | # 369 | ` 370 | 371 | const colon = 372 | ` 373 | 374 | # 375 | 376 | # 377 | 378 | ` 379 | 380 | const semicolon = 381 | ` 382 | 383 | # 384 | 385 | # 386 | # 387 | ` 388 | 389 | const openparen = 390 | ` 391 | # 392 | # 393 | # 394 | # 395 | # 396 | ` 397 | 398 | const closeparen = 399 | ` 400 | # 401 | # 402 | # 403 | # 404 | # 405 | ` 406 | 407 | const lessthan = 408 | ` 409 | # 410 | # 411 | # 412 | # 413 | # 414 | ` 415 | 416 | const greaterthan = 417 | ` 418 | # 419 | # 420 | # 421 | # 422 | # 423 | ` 424 | 425 | const one = ` 426 | ## 427 | # # 428 | # 429 | # 430 | ### 431 | ` 432 | 433 | const two = ` 434 | ## 435 | # # 436 | # 437 | # 438 | #### 439 | ` 440 | 441 | const three = ` 442 | ## 443 | # # 444 | # 445 | # # 446 | ## 447 | ` 448 | 449 | const four = ` 450 | # # 451 | # # 452 | #### 453 | # 454 | # 455 | ` 456 | 457 | const five = ` 458 | #### 459 | # 460 | ### 461 | # 462 | ### 463 | ` 464 | 465 | const six = ` 466 | # 467 | # 468 | ### 469 | # # 470 | ## 471 | ` 472 | 473 | const seven = ` 474 | #### 475 | # 476 | # 477 | # 478 | # 479 | ` 480 | 481 | const eight = ` 482 | ## 483 | # # 484 | ## 485 | # # 486 | ## 487 | ` 488 | 489 | const nine = ` 490 | ## 491 | # # 492 | ## 493 | # # 494 | ## 495 | ` 496 | 497 | const zero = ` 498 | ### 499 | # ## 500 | # # # 501 | ## # 502 | ### 503 | ` 504 | 505 | const letters = { 506 | a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, 507 | '!': exclaimation, 508 | '@': at, 509 | '#': hashtag, 510 | '$': dollar, 511 | '%': percent, 512 | '^': caret, 513 | '&': ampersand, 514 | '*': asterisk, 515 | '/': slash, 516 | '+': plus, 517 | '-': minus, 518 | '=': equals, 519 | '_': underscore, 520 | ' ': space, 521 | '.': period, 522 | ':': colon, 523 | ';': semicolon, 524 | '(': openparen, 525 | ')': closeparen, 526 | '<': lessthan, 527 | '>': greaterthan, 528 | '1': one, 529 | '2': two, 530 | '3': three, 531 | '4': four, 532 | '5': five, 533 | '6': six, 534 | '7': seven, 535 | '8': eight, 536 | '9': nine, 537 | '0': zero 538 | }; 539 | 540 | function daysLater (initial, days) { 541 | const timePassed = days * 24 * 60 * 60 * 1000; 542 | return new Date(new Date(initial).setDate(initial.getDate() + days)); 543 | } 544 | 545 | function MMDDYYYY (date) { 546 | return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`; 547 | } 548 | 549 | function MMDDYYdash (date) { 550 | return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`; 551 | } 552 | 553 | function type (year, message) { 554 | const sunday = new Date(year + '-1-' + (1 + 7 - new Date(year + '-1-1 0:00').getDay()) + ' 0:00'); 555 | const dates = []; 556 | const messageLetters = message.toLowerCase().split(''); 557 | let weeks = 0; 558 | for (const letter of messageLetters) { 559 | const output = makeLetter(letter, sunday, weeks); 560 | weeks = output.weeks; 561 | dates.push(...output.dates); 562 | } 563 | const commands = []; 564 | 565 | dates.forEach(date => { 566 | commands.push(`mkdir ContribGraphMessages || true`); 567 | commands.push(`echo "${Date.now()}" > ContribGraphMessages/${MMDDYYdash(date)}.txt`); 568 | commands.push(`echo "${MMDDYYdash(date) + '_' + Date.now()}" > ./logs.txt`); 569 | commands.push(`sleep 0`); 570 | commands.push('git add -A'); 571 | commands.push(`git commit -m "${date.toISOString()}" --date "${MMDDYYYY(date)}" --quiet`); 572 | }); 573 | 574 | return commands; 575 | } 576 | 577 | function makeLetter (letter, sunday, weeks) { 578 | if (!letters[letter]) return { dates: [], weeks }; 579 | weeks++; 580 | 581 | let maxLength = 0; 582 | letters[letter].split('\n').forEach(line => line.length > maxLength ? maxLength = line.length : 0); 583 | 584 | const dates = []; 585 | 586 | const lines = letters[letter].split('\n'); 587 | for (let i = 0; i < lines.length; i++) { 588 | const line = lines[i]; 589 | const chars = line.split(''); 590 | 591 | for (let j = 0; j < chars.length; j++) { 592 | const char = chars[j]; 593 | if (char === ' ') continue; 594 | 595 | const date = daysLater(sunday, weeks * 7 + j * 7 + i); 596 | 597 | dates.push(date); 598 | } 599 | } 600 | 601 | weeks += maxLength; 602 | 603 | return { 604 | dates, 605 | weeks 606 | } 607 | } 608 | 609 | module.exports = type; 610 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitwrite-cli", 3 | "version": "1.4.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "gitwrite-cli", 9 | "version": "1.4.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "node-fetch": "^2.6.7", 13 | "prompt-sync": "^4.2.0" 14 | }, 15 | "bin": { 16 | "gitwrite": "index.js", 17 | "gitwrite-cli": "index.js" 18 | } 19 | }, 20 | "node_modules/node-fetch": { 21 | "version": "2.6.7", 22 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 23 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 24 | "dependencies": { 25 | "whatwg-url": "^5.0.0" 26 | }, 27 | "engines": { 28 | "node": "4.x || >=6.0.0" 29 | }, 30 | "peerDependencies": { 31 | "encoding": "^0.1.0" 32 | }, 33 | "peerDependenciesMeta": { 34 | "encoding": { 35 | "optional": true 36 | } 37 | } 38 | }, 39 | "node_modules/prompt-sync": { 40 | "version": "4.2.0", 41 | "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", 42 | "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", 43 | "dependencies": { 44 | "strip-ansi": "^5.0.0" 45 | } 46 | }, 47 | "node_modules/prompt-sync/node_modules/ansi-regex": { 48 | "version": "4.1.1", 49 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", 50 | "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", 51 | "engines": { 52 | "node": ">=6" 53 | } 54 | }, 55 | "node_modules/prompt-sync/node_modules/strip-ansi": { 56 | "version": "5.2.0", 57 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 58 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 59 | "dependencies": { 60 | "ansi-regex": "^4.1.0" 61 | }, 62 | "engines": { 63 | "node": ">=6" 64 | } 65 | }, 66 | "node_modules/tr46": { 67 | "version": "0.0.3", 68 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 69 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 70 | }, 71 | "node_modules/webidl-conversions": { 72 | "version": "3.0.1", 73 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 74 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 75 | }, 76 | "node_modules/whatwg-url": { 77 | "version": "5.0.0", 78 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 79 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 80 | "dependencies": { 81 | "tr46": "~0.0.3", 82 | "webidl-conversions": "^3.0.0" 83 | } 84 | } 85 | }, 86 | "dependencies": { 87 | "node-fetch": { 88 | "version": "2.6.7", 89 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 90 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 91 | "requires": { 92 | "whatwg-url": "^5.0.0" 93 | } 94 | }, 95 | "prompt-sync": { 96 | "version": "4.2.0", 97 | "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", 98 | "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", 99 | "requires": { 100 | "strip-ansi": "^5.0.0" 101 | }, 102 | "dependencies": { 103 | "ansi-regex": { 104 | "version": "4.1.1", 105 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", 106 | "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" 107 | }, 108 | "strip-ansi": { 109 | "version": "5.2.0", 110 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 111 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 112 | "requires": { 113 | "ansi-regex": "^4.1.0" 114 | } 115 | } 116 | } 117 | }, 118 | "tr46": { 119 | "version": "0.0.3", 120 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 121 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 122 | }, 123 | "webidl-conversions": { 124 | "version": "3.0.1", 125 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 126 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 127 | }, 128 | "whatwg-url": { 129 | "version": "5.0.0", 130 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 131 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 132 | "requires": { 133 | "tr46": "~0.0.3", 134 | "webidl-conversions": "^3.0.0" 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "node-fetch": "^2.6.7", 4 | "prompt-sync": "^4.2.0" 5 | }, 6 | "name": "gitwrite-cli", 7 | "description": "Write something on your contribution graph!", 8 | "version": "1.4.0", 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "bin": { 14 | "gitwrite": "./index.js", 15 | "gitwrite-cli": "./index.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/YodaLightsabr/gitwrite.git" 20 | }, 21 | "keywords": [ 22 | "git" 23 | ], 24 | "author": "YodaLightsabr", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/YodaLightsabr/gitwrite/issues" 28 | }, 29 | "homepage": "https://github.com/YodaLightsabr/gitwrite#readme" 30 | } 31 | --------------------------------------------------------------------------------