├── .editorconfig
├── .eslintrc
├── .gitignore
├── Dockerfile
├── Procfile
├── README.md
├── app.json
├── config
├── custom-environment-variables.json
└── default.json
├── index.html
├── now.json
├── package.json
├── src
├── event.ts
├── index.ts
├── requests.ts
├── utils.ts
└── web.ts
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | end_of_line = lf
3 | indent_size = 4
4 | indent_style = space
5 | insert_final_newline = true
6 | charset = utf-8
7 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "babel",
3 | "parserOptions": {
4 | "ecmaVersion": 7,
5 | "sourceType": "module"
6 | },
7 | "env": {
8 | "jest": true
9 | },
10 | "rules": {
11 | "quotes": [
12 | "error",
13 | "single"
14 | ],
15 | "indent": [
16 | "error",
17 | 4,
18 | {
19 | "SwitchCase": 1
20 | }
21 | ],
22 | "comma-dangle": [
23 | "error",
24 | "always-multiline"
25 | ],
26 | "newline-after-var": "error",
27 | "semi": [
28 | "error",
29 | "never"
30 | ],
31 | "eqeqeq": [
32 | "error",
33 | "always"
34 | ]
35 | }
36 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Nodejs modules and can get from package.json
2 | node_modules
3 |
4 | # build out dist
5 | dist
6 |
7 | ## yarn files
8 | yarn-error.log
9 |
10 | cookies.json
11 | *.zip
12 |
13 | config/porduction.json
14 | config/default.json
15 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:10.5.0-alpine
2 |
3 | RUN npm install -g yarn && \
4 | mkdir -p /app
5 |
6 | COPY . /app
7 | WORKDIR /app
8 |
9 | RUN yarn && yarn run build && \
10 | echo "#!/bin/sh" > /start && \
11 | echo "set -e" >> /start && \
12 | echo 'STEAM_TOKEN=${STEAM_TOKEN//,/\", \"}' >> /start && \
13 | echo "sed -i \"s/YOUR_TOKEN/\$STEAM_TOKEN/g\" /app/config/default.json" >> /start && \
14 | echo "node /app/dist/index.js" >> /start && \
15 | chmod +x /start
16 |
17 | CMD ["sh", "/start"]
18 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node dist/web.js
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Steam 2018 年暑期小游戏刷分脚本
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | ## Heroku
11 | [](https://heroku.com/deploy?template=https://github.com/Indexyz/steam_2018_summer_game)
12 |
13 | ## 快速使用 (Linux)
14 | 使用前先在 steam 页面上加入星球
15 | ```bash
16 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
17 | bash
18 | nvm install 10
19 | npm install -g yarn
20 | git clone https://github.com/Indexyz/steam_2018_summer_game.git
21 | cd steam_2018_summer_game
22 | # 编辑 config/default.json
23 | # 去 https://steamcommunity.com/saliengame/gettoken 获取 token
24 | # "token": [
25 | # "YOUR_TOKEN"
26 | # ]
27 | # 将 YOUR_TOKEN 改为你的 TOKEN
28 | # 可以填写多个用户的 token
29 | yarn
30 | yarn run build
31 | node dist/index.js
32 | # Enjoy!
33 | ```
34 |
35 | ## 快速使用 (Docker)
36 | ```bash
37 | docker run --name steam_game -d --restart=always -e STEAM_TOKEN=你的TOKEN indexyz/steam_2018_summer_game
38 | ```
39 |
40 | ## Windows 下安装
41 | 安装 https://nodejs.org/zh-cn/ 和 https://yarnpkg.com/lang/zh-hans/ 然后 Download Zip 并解压
42 | ```bash
43 | # 编辑 config/default.json
44 | # 去 https://steamcommunity.com/saliengame/gettoken 获取 token
45 | # "token": [
46 | # "YOUR_TOKEN"
47 | # ]
48 | # 将 YOUR_TOKEN 改为你的 TOKEN
49 | # 可以填写多个用户的 token
50 | yarn
51 | yarn run build
52 | node dist/index.js
53 | # Enjoy!
54 | ```
55 | ```
56 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Steam 2018年 夏日小游戏",
3 | "description": "自动玩夏日小游戏",
4 | "env": {
5 | "USER_TOKEN": {
6 | "description": "用户Token",
7 | "value": ""
8 | },
9 | "WEB_PASS": {
10 | "description": "Web 界面密码",
11 | "value": "Password"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/config/custom-environment-variables.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": "USER_TOKEN",
3 | "webPass": "WEB_PASS"
4 | }
5 |
--------------------------------------------------------------------------------
/config/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": [
3 | "YOUR_TOKEN"
4 | ],
5 | "webPass": "Password"
6 | }
7 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Steam Game Logger
8 |
9 |
10 |
11 |
12 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "steam-2018-summer-game",
3 | "type": "npm",
4 | "engines": {
5 | "node": "8.11.2"
6 | },
7 | "env": {
8 | "USER_TOKEN": "",
9 | "WEB_PASS": "Password"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "steam_2018_summer_game",
3 | "version": "1.0.0",
4 | "main": "src/index.js",
5 | "repository": "git@github.com:Indexyz/steam_2018_summer_game.git",
6 | "author": "Indexyz ",
7 | "license": "MIT",
8 | "private": false,
9 | "scripts": {
10 | "build": "tsc",
11 | "type-check": "tsc --noEmit",
12 | "lint": "eslint src",
13 | "dev": "ts-node src/index.ts",
14 | "web": "ts-node dist/web.ts",
15 | "heroku-postbuild": "tsc",
16 | "now-start": "node dist/web.js"
17 | },
18 | "dependencies": {
19 | "axios": "^0.18.0",
20 | "chalk": "^2.4.1",
21 | "config": "^1.30.0",
22 | "koa": "^2.5.1",
23 | "koa-logger": "^3.2.0",
24 | "moment": "^2.22.2",
25 | "socket.io": "^2.1.1",
26 | "socketio-auth": "^0.1.1"
27 | },
28 | "devDependencies": {
29 | "@types/koa": "^2.0.46",
30 | "@types/node": "^10.3.3",
31 | "@types/socket.io": "^1.4.36",
32 | "eslint": "^4.19.1",
33 | "ts-node": "^7.0.0",
34 | "typescript": "^2.9.2"
35 | },
36 | "engines": {
37 | "node": ">=6.14.3"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/event.ts:
--------------------------------------------------------------------------------
1 | import * as event from 'events'
2 |
3 | const events = {
4 | LogEvent: Symbol(),
5 | }
6 |
7 | export default new event.EventEmitter()
8 | export { events }
9 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Utils from './utils'
2 | import Requests from './requests'
3 | import * as config from 'config'
4 |
5 | // get token from https://steamcommunity.com/saliengame/gettoken
6 | // can add many user
7 | const userList = config.get('token')
8 |
9 | let singleton: boolean = false
10 | if (userList.length === 1) {
11 | singleton = true
12 | }
13 |
14 | function getRandomInt(min: number, max: number): number {
15 | min = Math.ceil(min)
16 | max = Math.floor(max)
17 | return Math.floor(Math.random() * (max - min)) + min
18 | }
19 |
20 | async function SteamGame(userToken: string) {
21 | let loggerName = `Saliens (${userToken})`
22 | if (singleton) {
23 | loggerName = `Saliens`
24 | }
25 |
26 | const logger = new Utils.Logger(Utils.Logger.LEVEL_INFO, loggerName)
27 | const requests = new Requests.SalienGame(userToken, logger)
28 | let times = 0
29 |
30 | while (true) {
31 | const bossPlanet = await requests.getBossPlanet()
32 | if (bossPlanet !== null) {
33 | logger.info('>> Boss room founded, joining')
34 | await requests.joinPlanetRequest(bossPlanet.id)
35 | const planetInfo = await requests.getPlanetInfoRequest(bossPlanet.id)
36 | const zones = planetInfo.planets[0].zones
37 | .filter(item => !item.captured)
38 | .filter(item => item.type === 4)
39 | if (zones.length === 0) {
40 | continue
41 | }
42 | const zone = zones[0]
43 | await requests.joinBossZone(zone.zone_position)
44 | let damage = await requests.sendBossDamage()
45 | while (!damage.game_over) {
46 | await Utils.Time.wait(5 * Utils.Time.Second)
47 | damage = await requests.sendBossDamage()
48 | }
49 |
50 | continue
51 | }
52 |
53 | times += 1
54 | const playerInfo = await requests.getPlayerInfoRequest()
55 |
56 | const planet = await requests.getPlanetInfoRequest(playerInfo.active_planet)
57 |
58 | if (times % 10 === 1) {
59 | await requests.leaveRequest()
60 | logger.info('>> Left Planet, Joining New One')
61 | await requests.selectPlanetRequest()
62 | continue
63 | }
64 |
65 | if (planet.planets === undefined) {
66 | await requests.selectPlanetRequest()
67 | continue
68 | }
69 |
70 | const zones = planet.planets[0].zones
71 | .filter(item => !item.captured && item.top_clans)
72 |
73 | if (zones.length === 0) {
74 | logger.error(' No Available Zone Found, Please Change Your Planet')
75 | continue
76 | }
77 |
78 | const zone = zones
79 | .reduce((best, thisZone) => {
80 | if (thisZone.difficulty > best.difficulty) {
81 | return thisZone
82 | }
83 | return best
84 | })
85 |
86 | let post_score = 2400
87 | if (zone.difficulty === 1) {
88 | post_score = 585
89 | } else if (zone.difficulty === 2) {
90 | post_score = 1170
91 | }
92 |
93 | const scoreTable = [
94 | 0, // Level 1
95 | 1200, // Level 2
96 | 2400, // Level 3
97 | 4800, // Level 4
98 | 12000, // Level 5
99 | 30000, // Level 6
100 | 72000, // Level 7
101 | 180000, // Level 8
102 | 450000, // Level 9
103 | 1200000, // Level 10
104 | 2400000, // Level 11
105 | 3600000, // Level 12
106 | 4800000, // Level 13
107 | 6000000, // Level 14
108 | 7200000, // Level 15
109 | 8400000, // Level 16
110 | 9600000, // Level 17
111 | 10800000, // Level 18
112 | 12000000, // Level 19
113 | 14600000, // Level 20
114 | 16800000, // Level 21
115 | ]
116 |
117 | function getPercentage(current, goal) {
118 | const percentage = (Number(current) / Number(goal)) * 100
119 | return Math.round(percentage * 100) / 100
120 | }
121 |
122 | function timeConvert(mins) {
123 | const hours = (mins / 60)
124 | const rhours = Math.floor(hours)
125 | const minutes = (hours - rhours) * 60
126 | const rminutes = Math.round(minutes)
127 | return rhours + " hour(s) and " + rminutes + " minute(s)"
128 | }
129 |
130 | function getETA(current, goal, reward) {
131 | const remaining = (Number(goal) - Number(current)) / Number(reward)
132 | const time = remaining * 2
133 | return timeConvert(time)
134 | }
135 |
136 | logger.info(`>> User Level: ${playerInfo.level}`)
137 | logger.info(` Total Score: ${playerInfo.score}`)
138 | logger.info(` Level Progress: ${Number(playerInfo.score) - Number(scoreTable[playerInfo.level - 1])} ` +
139 | `out of ${Number(playerInfo.next_level_score) - Number(scoreTable[playerInfo.level - 1])} for level ${playerInfo.level + 1} ` +
140 | `(${getPercentage(playerInfo.score, playerInfo.next_level_score)}%)`)
141 | logger.info(` Until Next Level: ${getETA(playerInfo.score, playerInfo.next_level_score, post_score)}`)
142 | logger.info(` User Planet: ${planet.planets[0].state.name}`)
143 | logger.info(` Selected Zone: ${zone.gameid}`)
144 | logger.info(` Zone Reward Score: ${post_score}`)
145 | logger.info(' ')
146 |
147 | const joinMessage = await requests.joinZoneRequest(zone.zone_position);
148 | if (joinMessage.score === null) {
149 | logger.error(`!! Server Rejected Request`)
150 | continue
151 | }
152 | const randomSecond = 115 + getRandomInt(0, 5)
153 |
154 | logger.info(`>> Joined Zone, Waiting ${randomSecond} Seconds Until Submitting Score`)
155 | await Utils.Time.wait(randomSecond * Utils.Time.Second)
156 | logger.info('>> Submitting Score')
157 | let retryTimes: number = 0
158 |
159 | while (retryTimes <= 5) {
160 | retryTimes += 1
161 | logger.info(` Submitting Score (Retry ${retryTimes})`)
162 | const requestScore = await requests.postScoreRequest(post_score)
163 |
164 | if (requestScore.new_score === undefined) {
165 | logger.warn(' Received Undefined Response, Are There Multiple Instances Running?')
166 | await Utils.Time.wait((retryTimes * 2) * Utils.Time.Second)
167 | } else {
168 | logger.info(` Submitted Score (Now ${requestScore.new_score})`)
169 | break
170 | }
171 | }
172 | logger.info(' ')
173 | }
174 | }
175 |
176 | function userThread(userId: string) {
177 | SteamGame(userId).catch(err => {
178 | console.log(err)
179 | userThread(userId)
180 | })
181 | }
182 |
183 | if (typeof userList === "string") {
184 | singleton = true
185 | userThread(userList)
186 | } else {
187 | for (const user of userList) {
188 | userThread(user)
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/requests.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import Utils from './utils'
3 |
4 | const steamHost = 'https://community.steam-api.com'
5 | const apiEndpoint = `${steamHost}/ITerritoryControlMinigameService`
6 |
7 | type Planet = {
8 | giveaway_apps: Array,
9 | id: string,
10 | state: {
11 | activation_time: number,
12 | active: boolean,
13 | capture_progress: number,
14 | captured: boolean,
15 | cloud_filename: string,
16 | current_players: number,
17 | difficulty: number,
18 | giveaway_id: string,
19 | image_filename: string,
20 | land_filename: string,
21 | map_filename: string,
22 | name: string,
23 | position: number,
24 | priority: number,
25 | tag_ids: string,
26 | total_joins: number,
27 | }
28 | }
29 |
30 | type BossResponse = {
31 | boss_status: {
32 | boss_hp: number,
33 | boss_max_hp: number,
34 | boss_players: Array