├── assets ├── won.jpg └── slack.png ├── entrypoint.sh ├── renovate.json ├── tslint.json ├── tsconfig.json ├── Dockerfile ├── .github └── workflows │ └── main.yml ├── .releaserc.json ├── action.yml ├── .gitignore ├── index.ts ├── package.json ├── index.js ├── README.md └── CHANGELOG.md /assets/won.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooyoul/melon-ticket-actions/HEAD/assets/won.jpg -------------------------------------------------------------------------------- /assets/slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooyoul/melon-ticket-actions/HEAD/assets/slack.png -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | 3 | set -e 4 | 5 | NODE_PATH=/var/task/node_modules node /var/task/index.js 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@vingle", 4 | "@vingle:semantic-commit" 5 | ], 6 | "docker": { 7 | "enabled": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "object-literal-sort-keys": false, 9 | "interface-name": [true, "never-prefix"] 10 | }, 11 | "rulesDirectory": [] 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "noImplicitAny": true, 6 | "declaration": false, 7 | "sourceMap": false, 8 | "strictNullChecks": true 9 | }, 10 | "include": [ 11 | "index.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | MAINTAINER MooYeol Prescott Lee "mooyoul@gmail.com" 3 | 4 | RUN mkdir -p /var/task/ 5 | 6 | WORKDIR /var/task 7 | 8 | COPY package.json package-lock.json /var/task/ 9 | RUN npm ci --production 10 | 11 | COPY entrypoint.sh index.js /var/task/ 12 | 13 | ENTRYPOINT ["/var/task/entrypoint.sh"] 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: workflow 2 | on: push 3 | jobs: 4 | job: 5 | runs-on: ubuntu-latest 6 | timeout-minutes: 5 7 | steps: 8 | - uses: actions/checkout@v1 9 | - name: Prepare 10 | run: npm ci 11 | - name: Lint 12 | uses: mooyoul/tslint-actions@master 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | pattern: '*.ts' 16 | - name: Build 17 | run: npm run build 18 | - name: Publish 19 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 20 | run: npx semantic-release 21 | env: 22 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 23 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["@semantic-release/commit-analyzer", { 4 | "preset": "angular", 5 | "parserOpts": { 6 | "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"] 7 | } 8 | }], 9 | ["@semantic-release/release-notes-generator", { 10 | "preset": "angular", 11 | "parserOpts": { 12 | "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"] 13 | } 14 | }], 15 | ["@semantic-release/changelog", { 16 | "changelogFile": "CHANGELOG.md" 17 | }], 18 | "@semantic-release/github", 19 | ["@semantic-release/git", { 20 | "assets": ["CHANGELOG.md", "index.js"] 21 | }] 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Melon Ticket Notifier' 2 | description: 'GitHub action that notifies ticket availability' 3 | author: 'MooYeol Prescott Lee' 4 | inputs: 5 | product-id: 6 | description: 'Product ID of Target Ticket' 7 | required: true 8 | schedule-id: 9 | description: 'Schedule ID of Target Ticket' 10 | required: true 11 | seat-id: 12 | description: 'Seat Id of Target Ticket' 13 | required: true 14 | slack-incoming-webhook-url: 15 | description: 'URL of Slack Incoming Webhook' 16 | required: true 17 | message: 18 | description: 'Message' 19 | required: false 20 | default: '티켓사세요' 21 | runs: 22 | using: 'docker' 23 | image: 'Dockerfile' 24 | 25 | branding: 26 | icon: 'music' 27 | color: 'green' 28 | -------------------------------------------------------------------------------- /.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 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | import { IncomingWebhook } from "@slack/webhook"; 3 | import axios from "axios"; 4 | import * as qs from "querystring"; 5 | 6 | (async () => { 7 | // Validate parameters 8 | const [ productId, scheduleId, seatId, webhookUrl ] = [ 9 | "product-id", 10 | "schedule-id", 11 | "seat-id", 12 | "slack-incoming-webhook-url", 13 | ].map((name) => { 14 | const value = core.getInput(name); 15 | if (!value) { 16 | throw new Error(`melon-ticket-actions: Please set ${name} input parameter`); 17 | } 18 | 19 | return value; 20 | }); 21 | 22 | const message = core.getInput("message") ?? "티켓사세요"; 23 | 24 | const webhook = new IncomingWebhook(webhookUrl); 25 | 26 | const res = await axios({ 27 | method: "POST", 28 | url: "https://ticket.melon.com/tktapi/product/seatStateInfo.json", 29 | params: { 30 | v: "1", 31 | }, 32 | data: qs.stringify({ 33 | prodId: productId, 34 | scheduleNo: scheduleId, 35 | seatId, 36 | volume: 1, 37 | selectedGradeVolume: 1, 38 | }), 39 | }); 40 | 41 | // tslint:disable-next-line 42 | console.log("Got response: ", res.data); 43 | 44 | if (res.data.chkResult) { 45 | const link = `http://ticket.melon.com/performance/index.htm?${qs.stringify({ 46 | prodId: productId, 47 | })}`; 48 | 49 | await webhook.send(`${message} ${link}`); 50 | } 51 | })().catch((e) => { 52 | console.error(e.stack); // tslint:disable-line 53 | core.setFailed(e.message); 54 | }); 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "melon-ticket-actions", 3 | "version": "0.1.0", 4 | "description": "GitHub action that notifies ticket availability in melon ticket service", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "lint": "tslint index.ts" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/mooyoul/melon-ticket-actions.git" 13 | }, 14 | "keywords": [ 15 | "github-actions", 16 | "typescript" 17 | ], 18 | "author": "MooYeol Prescott Lee ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/mooyoul/melon-ticket-actions/issues" 22 | }, 23 | "homepage": "https://github.com/mooyoul/melon-ticket-actions#readme", 24 | "devDependencies": { 25 | "@semantic-release/changelog": "5.0.1", 26 | "@semantic-release/commit-analyzer": "8.0.1", 27 | "@semantic-release/exec": "5.0.0", 28 | "@semantic-release/git": "9.0.1", 29 | "@semantic-release/release-notes-generator": "9.0.3", 30 | "@types/node": "10.17.60", 31 | "@vingle/commitlint-preset": "1.0.3", 32 | "@vingle/tslint-preset": "1.0.1", 33 | "chai": "4.5.0", 34 | "husky": "4.3.8", 35 | "semantic-release": "17.4.7", 36 | "tslint": "5.20.1", 37 | "typescript": "3.9.10" 38 | }, 39 | "dependencies": { 40 | "@actions/core": "^1.1.1", 41 | "@slack/webhook": "^5.0.2", 42 | "axios": "^0.27.0" 43 | }, 44 | "config": { 45 | "commitizen": { 46 | "path": "cz-conventional-changelog" 47 | } 48 | }, 49 | "husky": { 50 | "hooks": { 51 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 52 | } 53 | }, 54 | "commitlint": { 55 | "extends": [ 56 | "@vingle/commitlint-preset" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const core = require("@actions/core"); 4 | const webhook_1 = require("@slack/webhook"); 5 | const axios_1 = require("axios"); 6 | const qs = require("querystring"); 7 | (async () => { 8 | var _a; 9 | // Validate parameters 10 | const [productId, scheduleId, seatId, webhookUrl] = [ 11 | "product-id", 12 | "schedule-id", 13 | "seat-id", 14 | "slack-incoming-webhook-url", 15 | ].map((name) => { 16 | const value = core.getInput(name); 17 | if (!value) { 18 | throw new Error(`melon-ticket-actions: Please set ${name} input parameter`); 19 | } 20 | return value; 21 | }); 22 | const message = (_a = core.getInput("message")) !== null && _a !== void 0 ? _a : "티켓사세요"; 23 | const webhook = new webhook_1.IncomingWebhook(webhookUrl); 24 | const res = await axios_1.default({ 25 | method: "POST", 26 | url: "https://ticket.melon.com/tktapi/product/seatStateInfo.json", 27 | params: { 28 | v: "1", 29 | }, 30 | data: qs.stringify({ 31 | prodId: productId, 32 | scheduleNo: scheduleId, 33 | seatId, 34 | volume: 1, 35 | selectedGradeVolume: 1, 36 | }), 37 | }); 38 | // tslint:disable-next-line 39 | console.log("Got response: ", res.data); 40 | if (res.data.chkResult) { 41 | const link = `http://ticket.melon.com/performance/index.htm?${qs.stringify({ 42 | prodId: productId, 43 | })}`; 44 | await webhook.send(`${message} ${link}`); 45 | } 46 | })().catch((e) => { 47 | console.error(e.stack); // tslint:disable-line 48 | core.setFailed(e.message); 49 | }); 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # melon-ticket-actions 2 | 3 | [![Build Status](https://github.com/mooyoul/melon-ticket-actions/workflows/workflow/badge.svg)](https://github.com/mooyoul/melon-ticket-actions/actions) 4 | [![Semantic Release enabled](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 5 | [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) 6 | [![MIT license](http://img.shields.io/badge/license-MIT-blue.svg)](http://mooyoul.mit-license.org/) 7 | 8 | GitHub action that checks ticket availability in Melon Ticket (Korean online ticket store) website. 9 | 10 | ~~암표상들 다 망해라~~ 그리핀 내한공연 보게 해주세요 🙏 11 | 12 | ----- 13 | 14 | ![slack](assets/slack.png) 15 | 16 | ### ⭐️ 존버는 승리한다 ⭐️ 17 | 18 | ![won](assets/won.jpg) 19 | 20 | ## Usage 21 | 22 | Please see [example workflow](https://github.com/mooyoul/melon-ticket-actions/blob/1c5a56b9cdd594051d856c16b020f0c5835f6955/.github/workflows/example.yml), or [Workflow Log](https://github.com/mooyoul/melon-ticket-actions/actions?query=workflow%3Aexample) 23 | 24 | ## Sample Github Actions Configuration 25 | 26 | ```yaml 27 | name: example 28 | on: 29 | schedule: 30 | - cron: '*/5 * * * *' # Every 5 minutes 31 | jobs: 32 | job: 33 | runs-on: ubuntu-latest 34 | timeout-minutes: 5 35 | steps: 36 | - name: Check Tickets 37 | uses: mooyoul/melon-ticket-actions@v1.1.0 38 | with: 39 | product-id: 204755 40 | schedule-id: 100001 41 | seat-id: 1_0 42 | slack-incoming-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} 43 | message: '<@U12345678> 달려달려~' 44 | ``` 45 | 46 | ## License 47 | 48 | [MIT](LICENSE) 49 | 50 | See full license on [mooyoul.mit-license.org](http://mooyoul.mit-license.org/) 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.5](https://github.com/mooyoul/melon-ticket-actions/compare/v1.1.4...v1.1.5) (2022-01-18) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * **deps:** update dependency axios to ^0.25.0 ([71f790e](https://github.com/mooyoul/melon-ticket-actions/commit/71f790e8ed33bde762c7dce57e65060b312ff768)) 7 | 8 | ## [1.1.4](https://github.com/mooyoul/melon-ticket-actions/compare/v1.1.3...v1.1.4) (2021-10-25) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * **deps:** update dependency axios to ^0.24.0 ([71b9fe6](https://github.com/mooyoul/melon-ticket-actions/commit/71b9fe6ece93e9996a99f49d4a2356977167e39b)) 14 | 15 | ## [1.1.3](https://github.com/mooyoul/melon-ticket-actions/compare/v1.1.2...v1.1.3) (2020-10-23) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **deps:** update dependency axios to ^0.21.0 ([95c7199](https://github.com/mooyoul/melon-ticket-actions/commit/95c7199a8f2528b62c721562a8f1ff3b467c9d22)) 21 | 22 | ## [1.1.2](https://github.com/mooyoul/melon-ticket-actions/compare/v1.1.1...v1.1.2) (2020-10-01) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **deps:** update dependency @actions/core to v1.2.6 [security] ([b646738](https://github.com/mooyoul/melon-ticket-actions/commit/b646738f74a696ede2941053319b788ba00a055f)) 28 | 29 | ## [1.1.1](https://github.com/mooyoul/melon-ticket-actions/compare/v1.1.0...v1.1.1) (2020-08-21) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * **deps:** update dependency axios to ^0.20.0 ([bb64155](https://github.com/mooyoul/melon-ticket-actions/commit/bb64155bc60e749770e260674614105b7c334e13)) 35 | 36 | # [1.1.0](https://github.com/mooyoul/melon-ticket-actions/compare/v1.0.1...v1.1.0) (2019-12-16) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * fix missing querystring indicator ([874e35a](https://github.com/mooyoul/melon-ticket-actions/commit/874e35a14292ea6734197f4f41c97c878847c930)) 42 | 43 | 44 | ### Features 45 | 46 | * support custom message ([e6db3f4](https://github.com/mooyoul/melon-ticket-actions/commit/e6db3f496881ecf2b04e92d1862073620d53ae17)) 47 | 48 | ## [1.0.1](https://github.com/mooyoul/melon-ticket-actions/compare/v1.0.0...v1.0.1) (2019-12-15) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * fix invalid method ([e743bd5](https://github.com/mooyoul/melon-ticket-actions/commit/e743bd57f1ae77855ccd1aff1121efdde4467ad3)) 54 | * remove hard-coded product url ([5891c4a](https://github.com/mooyoul/melon-ticket-actions/commit/5891c4aa4a1f270b6ad237672544e8b397d5fb42)) 55 | 56 | # 1.0.0 (2019-12-15) 57 | 58 | 59 | ### Features 60 | 61 | * initial commit ([b0ef7a7](https://github.com/mooyoul/melon-ticket-actions/commit/b0ef7a7b3a1fda287de14d87a0e5c5bd1abeedde)) 62 | --------------------------------------------------------------------------------