Several new features and bugfixes
Toying with a postgres storage solution (make getUser async to prepare). Post oneShot images from local files instead of just external links. !tboi
|
@ -1,6 +1,10 @@
|
|||
script.sh
|
||||
.idea/
|
||||
backups/
|
||||
hvacoins.json
|
||||
hvackerconfig.json
|
||||
users.json
|
||||
standup.json
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
|
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 20 KiB |
|
@ -14,7 +14,8 @@
|
|||
"base-64": "^1.0.0",
|
||||
"express": "^4.17.3",
|
||||
"fs": "0.0.1-security",
|
||||
"jest": "27.0.6"
|
||||
"jest": "27.0.6",
|
||||
"pg": "^8.11.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
|
@ -1800,6 +1801,14 @@
|
|||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
@ -4619,6 +4628,11 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
|
@ -4691,6 +4705,89 @@
|
|||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"dependencies": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
|
@ -4739,6 +4836,41 @@
|
|||
"semver-compare": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
|
@ -5172,6 +5304,14 @@
|
|||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
|
@ -5764,6 +5904,14 @@
|
|||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
@ -7132,6 +7280,11 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
@ -9067,6 +9220,11 @@
|
|||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||
},
|
||||
"packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
|
@ -9113,6 +9271,68 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"requires": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-cloudflare": "^1.1.1",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
}
|
||||
},
|
||||
"pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||
},
|
||||
"pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
|
||||
},
|
||||
"pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"requires": {}
|
||||
},
|
||||
"pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
|
||||
},
|
||||
"pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"requires": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"requires": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
|
@ -9144,6 +9364,29 @@
|
|||
"semver-compare": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
|
||||
},
|
||||
"postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
|
||||
},
|
||||
"postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
|
||||
},
|
||||
"postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
|
@ -9427,6 +9670,11 @@
|
|||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
|
@ -9816,6 +10064,11 @@
|
|||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"base-64": "^1.0.0",
|
||||
"express": "^4.17.3",
|
||||
"fs": "0.0.1-security",
|
||||
"jest": "27.0.6"
|
||||
"jest": "27.0.6",
|
||||
"pg": "^8.11.3"
|
||||
},
|
||||
"standard": {
|
||||
"env": "jest"
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
export PGHOST=localhost
|
||||
export PGUSER="postgres"
|
||||
export PGDATABASE="postgres"
|
||||
export PGPASSWORD="thisisthesoundofamannamedpostgres"
|
||||
export PGPORT=5432
|
||||
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const buyableItems = require('./buyableItems')
|
||||
const { commas, setHighestCoins, addAchievement, getUser, singleItemCps, chaosFilter, fuzzyMatcher, calculateCost } = require('./utils')
|
||||
const { commas, setHighestCoins, addAchievement, getUser, singleItemCps, chaosFilter, fuzzyMatcher, calculateCost, saveUser } = require('./utils')
|
||||
const slack = require('../../slack')
|
||||
|
||||
const leaderboardUpdater = {}
|
||||
|
@ -153,6 +153,7 @@ const buyRoute = async ({ event, say, args, user }) => {
|
|||
|
||||
const countString = quantity === 1 ? 'one' : quantity
|
||||
await say(`You bought ${countString} :${buyableItem.emoji}:`)
|
||||
await saveUser(event.user, user, `Buying ${countString} :${buyableItem.emoji}:`)
|
||||
}
|
||||
|
||||
const buyButton = async ({ body, ack, say, payload }) => {
|
||||
|
@ -162,7 +163,7 @@ const buyButton = async ({ body, ack, say, payload }) => {
|
|||
const event = {
|
||||
user: body.user.id
|
||||
}
|
||||
const user = getUser(event.user)
|
||||
const user = await getUser(event.user)
|
||||
const words = ['', buying, body.actions[0].text]
|
||||
const [commandName, ...args] = words
|
||||
|
||||
|
@ -177,6 +178,7 @@ const buyButton = async ({ body, ack, say, payload }) => {
|
|||
...buyText2(highestCoins, user, extraMessage)
|
||||
})
|
||||
await leaderboardUpdater.updateAllLeaderboards()
|
||||
await saveUser(event.user, user, `buyButton ${buying} clicked`)
|
||||
}
|
||||
|
||||
Object.keys(buyableItems).forEach(itemName => slack.app.action('buy_' + itemName, buyButton))
|
||||
|
|
|
@ -136,7 +136,7 @@ const addInteraction = ({ actionId, perform }) =>
|
|||
try {
|
||||
await ack()
|
||||
game.pet ??= makePet()
|
||||
const [everyone, local] = perform(game.pet, getUser(body.user.id))
|
||||
const [everyone, local] = perform(game.pet, await getUser(body.user.id))
|
||||
await updateEveryone(everyone)
|
||||
if (local) {
|
||||
await say(local)
|
||||
|
|
|
@ -8,6 +8,8 @@ const {
|
|||
idFromWord,
|
||||
getCoins,
|
||||
getUser,
|
||||
getUserSync,
|
||||
saveUser,
|
||||
commas,
|
||||
addAchievement,
|
||||
shufflePercent,
|
||||
|
@ -25,13 +27,15 @@ const {
|
|||
userHasCheckedQuackgrade,
|
||||
fuzzyMatcher,
|
||||
addCoins,
|
||||
game, updateAll
|
||||
game,
|
||||
updateAll,
|
||||
logMemoryUsage
|
||||
} = require('./utils')
|
||||
const { nfts, squad, users, horrors, stonkMarket, pet } = game
|
||||
const pets = require('./gotcha')
|
||||
|
||||
const exec = require('child_process').exec
|
||||
const { createReadStream, createWriteStream, existsSync, readFileSync} = require('fs')
|
||||
const { createReadStream, createWriteStream, existsSync, readFileSync, writeFileSync } = require('fs')
|
||||
const { readdir } = require('fs/promises')
|
||||
|
||||
const slack = require('../../slack')
|
||||
|
@ -80,7 +84,9 @@ const upgradeText = (user, showOwned = false) => {
|
|||
const hasUpgrade = (user, upgrade, upgradeName) => !!user.upgrades[upgrade.type]?.includes(upgradeName)
|
||||
|
||||
const alwaysAccessible = () => true
|
||||
|
||||
const alwaysAlwaysAccessible = () => true
|
||||
|
||||
const adminOnly = {
|
||||
hidden: true,
|
||||
condition: ({ event, say }) => {
|
||||
|
@ -91,10 +97,12 @@ const adminOnly = {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const testOnly = {
|
||||
hidden: true,
|
||||
condition: ({ event }) => event.user.includes('TEST')
|
||||
}
|
||||
|
||||
const dmsOnly = {
|
||||
hidden: false,
|
||||
condition: async ({ event, say, commandName }) => {
|
||||
|
@ -105,6 +113,7 @@ const dmsOnly = {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const prestigeOnly = {
|
||||
hidden: false,
|
||||
condition: async ({ event, say, commandName, user }) => {
|
||||
|
@ -181,6 +190,37 @@ const postCard = async (event, name, fileName) =>
|
|||
file: createReadStream(fileName)
|
||||
})
|
||||
|
||||
const findLineStartingWith = (prefix, text) => {
|
||||
const lines = text.split(/\s*\n\s*/g)
|
||||
const line = lines.filter(line => line.startsWith(prefix))[0]
|
||||
if (!line) {
|
||||
return
|
||||
}
|
||||
return line.substring(prefix.length)
|
||||
}
|
||||
|
||||
const findLinesNotStartingWith = (prefixes, text) => {
|
||||
const lines = text.split(/\s*\n\s*/g)
|
||||
const lineStartsWithAnyPrefix = line => prefixes.some(prefix => line.startsWith(prefix))
|
||||
return lines.filter(line => !lineStartsWithAnyPrefix(line)).join('\n')
|
||||
}
|
||||
|
||||
const isaacData = (() => {
|
||||
const parsed = JSON.parse(readFileSync('isaac-processed.json'))
|
||||
parsed.allItems = [...parsed.items, ...parsed.cards, ...parsed.trinkets]
|
||||
parsed.allItems = Object.fromEntries(parsed.allItems.map(item => [item.name.toLowerCase().trim(), {
|
||||
...item,
|
||||
itemId: findLineStartingWith('ItemID: ', item.text),
|
||||
quality: findLineStartingWith('Quality: ', item.text),
|
||||
type: findLineStartingWith('Type: ', item.text),
|
||||
rechargeTime: findLineStartingWith('Recharge Time: ', item.text),
|
||||
itemPools: findLineStartingWith('Item Pool: ', item.text)?.split(', '),
|
||||
description: findLinesNotStartingWith(['ItemID', 'Quality: ', 'Type: ', 'Recharge Time: ', 'Item Pool: '], item.text)
|
||||
}]))
|
||||
writeFileSync('isaac-new.json', JSON.stringify(parsed.allItems, null, 2))
|
||||
return parsed
|
||||
})()
|
||||
|
||||
const cardGames = {
|
||||
digimon: {
|
||||
names: ['!digimon', '!digi'],
|
||||
|
@ -191,7 +231,7 @@ const cardGames = {
|
|||
getCardImageUrl: card => card.image_url
|
||||
},
|
||||
pokemon: {
|
||||
names: ['!pokemon', '!poke', '!pok', '!yugioh', '!ygo'],
|
||||
names: ['!pokemon', '!poke', '!pok'],
|
||||
help: 'Search for Pokemon cards: !pok <card name>',
|
||||
fetch: async name => `https://api.pokemontcg.io/v2/cards?q=name:${name}`,
|
||||
getCardData: ({ data }) => data,
|
||||
|
@ -199,7 +239,7 @@ const cardGames = {
|
|||
getCardImageUrl: card => card.images.large
|
||||
},
|
||||
yugioh: {
|
||||
names: [],
|
||||
names: ['!yugioh', '!ygo'],
|
||||
help: 'Search for Yu-Gi-Oh cards: !ygo <card name>',
|
||||
fetch: async name => {
|
||||
const url = `https://db.ygoprodeck.com/api/v7/cardinfo.php?fname=${name}`;
|
||||
|
@ -210,42 +250,33 @@ const cardGames = {
|
|||
getCardName: card => card.name,
|
||||
getCardImageUrl: card => card.card_images[0].image_url
|
||||
},
|
||||
// lotrOld: {
|
||||
// names: ['!lotrOld'],
|
||||
// help: 'Search for Lord of the Rings cards: !lotrOld <card name>',
|
||||
// cards: async () => JSON.parse(readFileSync('lotrOld/lotr_cards.json').toString()),
|
||||
// fetch: async name => ({ json: () => {
|
||||
// const matcher = fuzzyMatcher(name?.toLowerCase())
|
||||
// const exact = cardGames.lotrOld.cards.filter(card => card.name?.toLowerCase() === name.toLowerCase())
|
||||
// if (exact.length) {
|
||||
// return exact
|
||||
// }
|
||||
// return cardGames.lotrOld.cards.filter(card => matcher.test(card.name))
|
||||
// } }),
|
||||
// getCardData: data => data,
|
||||
// getCardName: card => card.name,
|
||||
// getCardImageUrl: card => 'https://ringsdb.com' + card.imagesrc
|
||||
// },
|
||||
// lotr: {
|
||||
// names: ['!lotr'],
|
||||
// help: 'Search for Lord of the Rings cards: !lotr <card name>',
|
||||
// cards: async () => JSON.parse(readFileSync('lotr/cards.json').toString()),
|
||||
// fetch: async name => ({ json: () => {
|
||||
// const matcher = fuzzyMatcher(name?.toLowerCase())
|
||||
// const exact = cardGames.lotr.cards.filter(card => card.name?.toLowerCase() === name.toLowerCase())
|
||||
// if (exact.length) {
|
||||
// return [exact[0]]
|
||||
// }
|
||||
// return cardGames.lotr.cards.filter(card => matcher.test(card.name)).filter(card => !card.name.includes('('))
|
||||
// } }),
|
||||
// getCardData: data => data,
|
||||
// getCardName: card => card.name,
|
||||
// getCardImageUrl: card => 'https://lotrtcgwiki.com/wiki/_media/cards:' + card.id + '.jpg'
|
||||
// },
|
||||
playingCards: {
|
||||
names: ['!playing', '!pc'],
|
||||
help: 'Search for playing cards cards: !pc <card name>',
|
||||
cards: async () => readdir('playingCards')
|
||||
},
|
||||
isaac: {
|
||||
names: ['!isaac', '!tboi'],
|
||||
help: 'Search for TBOI items, cards, and trinkets',
|
||||
cards: async () => Object.keys(isaacData.allItems),
|
||||
fetch: async (name, say) => {
|
||||
name = name.toLowerCase()
|
||||
let matches = [isaacData.allItems[name]].filter(Boolean)
|
||||
if (!matches.length) {
|
||||
matches = Object.values(isaacData.allItems).filter(item => item.name.toLowerCase().includes(name))
|
||||
}
|
||||
if (!matches.length) {
|
||||
matches = Object.values(isaacData.allItems).filter(item => item.text.toLowerCase().includes(name))
|
||||
}
|
||||
//say('```' + JSON.stringify(matches, null, 2) + '```')
|
||||
if (!matches.length) {
|
||||
await say('No matches found!')
|
||||
return
|
||||
}
|
||||
const etcText = matches.length == 1 ? "" : `and ${matches.length - 1} other matches`
|
||||
const match = matches[0]
|
||||
await say(match.name + ': ' + match.description.replaceAll('\t', '').replaceAll(/ +/g, '').replaceAll(/\n\*,.*/g, ''))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,11 +287,13 @@ Object.entries(cardGames).forEach(async ([gameName, cardGame]) => {
|
|||
command(
|
||||
cardGame.names,
|
||||
cardGame.help,
|
||||
async ({ args, event, say }) => {
|
||||
const arg = args?.join(' ')
|
||||
if (!args?.length || !arg) {
|
||||
return say('Please specify a card name!')
|
||||
}
|
||||
async ({ args, event, say }) => args?.join(' ').split(',').forEach(async arg => {
|
||||
// const arg = args?.join(' ')
|
||||
// if (!args?.length || !arg) {
|
||||
// return say('Please specify a card name!')
|
||||
// }
|
||||
arg = arg.trim()
|
||||
console.log('arg', arg)
|
||||
if (cardGame.cards && !cardGame.fetch) {
|
||||
const fileName = cardGame.cards.find(name => name?.toLowerCase().replaceAll(/_/g, ' ').startsWith(arg.toLowerCase()))
|
||||
if (fileName) {
|
||||
|
@ -268,10 +301,13 @@ Object.entries(cardGames).forEach(async ([gameName, cardGame]) => {
|
|||
}
|
||||
return
|
||||
}
|
||||
let response = await cardGame.fetch(arg)
|
||||
let response = await cardGame.fetch(arg, say)
|
||||
if (typeof response === 'string') {
|
||||
response = await fetch(response)
|
||||
}
|
||||
if (!response) {
|
||||
return
|
||||
}
|
||||
const json = await response.json()
|
||||
const data = cardGame.getCardData(json)
|
||||
if (!data?.length) {
|
||||
|
@ -307,7 +343,7 @@ Object.entries(cardGames).forEach(async ([gameName, cardGame]) => {
|
|||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
hidden: false,
|
||||
condition: alwaysAlwaysAccessible
|
||||
|
@ -437,7 +473,6 @@ command(
|
|||
return say(`Silly, silly, ${user.name}.\nYou can't just leave us again.`)
|
||||
}
|
||||
user.isDisabled = true
|
||||
//saveGame()
|
||||
return say('.')
|
||||
}, { hidden: true })
|
||||
|
||||
|
@ -523,6 +558,7 @@ command(
|
|||
)
|
||||
|
||||
const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) => {
|
||||
console.log('messageHandler')
|
||||
if (event?.subtype === 'bot_message') {
|
||||
return botMessageHandler({ event, say })
|
||||
}
|
||||
|
@ -530,7 +566,8 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) =
|
|||
const words = event?.text?.split(/\s+/) || []
|
||||
const [commandName, ...args] = words
|
||||
const c = commands.get(commandName)
|
||||
let user = getUser(event.user)
|
||||
console.log('getUser')
|
||||
let user = await getUser(event.user)
|
||||
if (user.isDisabled && c.condition !== alwaysAlwaysAccessible) {
|
||||
return
|
||||
}
|
||||
|
@ -573,7 +610,7 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) =
|
|||
haunted = true
|
||||
const [disabledId] = getRandomFromArray(disabledUsers)
|
||||
event.user = disabledId
|
||||
user = getUser(event.user)
|
||||
user = await getUser(event.user)
|
||||
const userInfo = await slack.app.client.users.info({
|
||||
user: disabledId
|
||||
})
|
||||
|
@ -596,6 +633,7 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) =
|
|||
}
|
||||
}
|
||||
}
|
||||
console.log('getCoins')
|
||||
Object.entries(users).forEach(([id, usr]) => usr.coins = getCoins(id))
|
||||
//user.coins = getCoins(event.user)
|
||||
const isAdmin = event.user?.includes(slack.users.Admin)
|
||||
|
@ -635,6 +673,7 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) =
|
|||
return
|
||||
}
|
||||
|
||||
const before = JSON.stringify(user)
|
||||
await c.action({ event, say, trueSay, words, args, commandName, user, userId: event.user, haunted, isAdmin })
|
||||
if (!isRecycle) {
|
||||
const userQuackgrades = (user.quackUpgrades?.lightning || []).map(name => quackStore[name])
|
||||
|
@ -648,8 +687,13 @@ const messageHandler = async ({ event, say, isRecycle = false, skipCounting }) =
|
|||
setTimeout(() => lightning({ channel: event.channel, say, trueSay, words, user }), 10000)
|
||||
}
|
||||
}
|
||||
const after = JSON.stringify(user)
|
||||
const endTime = new Date()
|
||||
saveGame(`command ${event.text} finished in ${endTime - startTime}ms`)
|
||||
if (before !== after) {
|
||||
await saveUser(event.user, user, `command ${event.text} finished in ${endTime - startTime}ms`)
|
||||
} else {
|
||||
console.error(event.user, user, `command ${event.text} requested a redundant user save!`)
|
||||
}
|
||||
}
|
||||
|
||||
slack.onReaction(async ({ event }) => {
|
||||
|
@ -732,7 +776,7 @@ command(
|
|||
'Send a lighting strike to the given player.',
|
||||
async({ args, say, }) => {
|
||||
const targetId = idFromWord(args[0])
|
||||
await lightning({ user: getUser(targetId), ms: 15000, channel: targetId})
|
||||
await lightning({ user: await getUser(targetId), ms: 15000, channel: targetId})
|
||||
return say(`Sent a bolt of lighting to <@${targetId}>`)
|
||||
}, adminOnly)
|
||||
|
||||
|
@ -742,14 +786,14 @@ command(
|
|||
async ({ say, args }) => {
|
||||
// await dedicatedPlayers.forEach(async player => {
|
||||
// await lightning({
|
||||
// user: getUser(player),
|
||||
// user: await getUser(player),
|
||||
// ms: 30000,
|
||||
// channel: player
|
||||
// })
|
||||
// })
|
||||
const targetId = idFromWord(args[0])
|
||||
for (let i = 0; i < 10; i++) {
|
||||
setTimeout(async () => await lightning({ user: getUser(targetId), ms: 1600, channel: targetId, multiplier: 0.02}), i * 1500)
|
||||
setTimeout(async () => await lightning({ user: await getUser(targetId), ms: 1600, channel: targetId, multiplier: 0.02}), i * 1500)
|
||||
}
|
||||
return say(`Sent a lighting storm to <@${targetId}>`)
|
||||
// return say(`Sent a bolt of lighting to the dedicated players`)
|
||||
|
@ -761,7 +805,7 @@ slack.app.action('lightningStrike', async ({ body, ack }) => {
|
|||
return
|
||||
}
|
||||
const c = getCoins(body.user.id)
|
||||
const user = getUser(body.user.id)
|
||||
const user = await getUser(body.user.id)
|
||||
const secondsOfCps = seconds => Math.floor(getCPS(user) * seconds)
|
||||
let payout = secondsOfCps(60 * 30)
|
||||
if (payout > (0.2 * c)) {
|
||||
|
@ -771,7 +815,7 @@ slack.app.action('lightningStrike', async ({ body, ack }) => {
|
|||
payout = (500 + chaosFilter(payout, 1, user)) * strikes[body.message.ts]
|
||||
addCoins(user, (c + payout) - user.coins)
|
||||
delete strikes[body.message.ts]
|
||||
saveGame('user bottled a lightning strike')
|
||||
saveUser(body.user.id, user, 'bottled a lightning strike')
|
||||
|
||||
await slack.app.client.chat.update({
|
||||
channel: body.channel.id,
|
||||
|
@ -795,7 +839,7 @@ command(
|
|||
['!cleanusers'],
|
||||
'Calls getUser() on all users, ensuring a valid state.',
|
||||
async ({ say }) => {
|
||||
Object.keys(users).forEach(userId => { getUser(userId) })
|
||||
Object.keys(users).forEach(async userId => { await getUser(userId) })
|
||||
return say('```Cleaning ' + JSON.stringify(Object.keys(users), null, 2) + '```')
|
||||
}, adminOnly)
|
||||
|
||||
|
@ -916,7 +960,6 @@ command(
|
|||
return say(`Your password may not contain spaces!`)
|
||||
}
|
||||
user.pwHash = webapi.makeHash(args[0])
|
||||
//saveGame()
|
||||
await say(`Password encoded as ${user.pwHash}`)
|
||||
}
|
||||
, { hidden: true })
|
||||
|
@ -946,7 +989,6 @@ command(
|
|||
user.coins -= cost
|
||||
horrors.hvackerHelp += 1
|
||||
horrors.hvackerLast = dayOfYear()
|
||||
//saveGame()
|
||||
await say('I feel a bit better. Thank you...')
|
||||
}, { hidden: true })
|
||||
|
||||
|
@ -982,7 +1024,7 @@ command(
|
|||
async ({ say, args }) => {
|
||||
const achName = args[0]
|
||||
const target = idFromWord(args[1])
|
||||
await removeAchievement(getUser(target), achName, say)
|
||||
await removeAchievement(await getUser(target), achName, say)
|
||||
}, adminOnly)
|
||||
|
||||
command(
|
||||
|
@ -1055,7 +1097,7 @@ command(['!cps'],
|
|||
// }
|
||||
// } catch (e) {}
|
||||
//
|
||||
// const text = await doMine({ user: getUser(event.user), userId: event.user, say })
|
||||
// const text = await doMine({ user: await getUser(event.user), userId: event.user, say })
|
||||
// console.log('miningHeat:', miningHeat)
|
||||
// if (miningHeat < maxTemp) {
|
||||
// console.log(`${slack.users[event.user]} is pick mining.`);
|
||||
|
@ -1112,7 +1154,6 @@ const doMine = async ({ user, userId, say }) => {
|
|||
prefix = `You mined ${commas(diff)} HVAC.\n`
|
||||
}
|
||||
addCoins(user, diff)
|
||||
//saveGame()
|
||||
return `${prefix}You now have ${commas(user.coins)} HVAC coin${c !== 1 ? 's' : ''}. Spend wisely.`
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1183,7 @@ command(
|
|||
async ({ event, args, trueSay }) => {
|
||||
const [impersonating, ...newWords] = args
|
||||
event.user = idFromWord(impersonating)
|
||||
getUser(event.user)
|
||||
await getUser(event.user)
|
||||
const isDisabled = users[event.user].isDisabled
|
||||
users[event.user].isDisabled = false
|
||||
event.text = newWords.join(' ')
|
||||
|
@ -1154,7 +1195,7 @@ command(
|
|||
['!enable'],
|
||||
'Enable the given user',
|
||||
async ({ args }) => {
|
||||
const user = getUser(idFromWord(args[0]))
|
||||
const user = await getUser(idFromWord(args[0]))
|
||||
if (user.isDisabled) {
|
||||
user.isDisabled = false
|
||||
//saveGame()
|
||||
|
@ -1167,7 +1208,7 @@ command(
|
|||
['!disable'],
|
||||
'Disable the given user',
|
||||
async ({ args }) => {
|
||||
const user = getUser(idFromWord(args[0]))
|
||||
const user = await getUser(idFromWord(args[0]))
|
||||
user.isDisabled = true
|
||||
}, adminOnly)
|
||||
|
||||
|
@ -1381,7 +1422,7 @@ const upgradeButton = async ({ body, ack, say, payload }) => {
|
|||
const event = {
|
||||
user: body.user.id
|
||||
}
|
||||
const user = getUser(event.user, true)
|
||||
const user = await getUser(event.user, true)
|
||||
const words = ['!upgrade', upgrade]
|
||||
const [commandName, ...args] = words
|
||||
let extraMessage = ''
|
||||
|
@ -1423,9 +1464,18 @@ const squadText = () => {
|
|||
`:${emoji}: *${name}* - ${commas(remaining)} HVAC remaining.\n_${description}_`
|
||||
return currentUpgradeText(current)
|
||||
}
|
||||
return 'No more squadgrades currently available.'
|
||||
return 'All squadgrades are unlocked!\n\n' + Object.values(squadUpgrades).map(({ name, cost, emoji, description }) =>
|
||||
`:${emoji}: *${name}* - ${commas(cost)} HVAC.\n_${description}_`).join('\n\n')
|
||||
}
|
||||
|
||||
command(
|
||||
['!cat'],
|
||||
'View your total all-time coins collected',
|
||||
async ({ say, user }) => {
|
||||
await say(`You've earned a total of ${commas(user.coinsAllTime)} HVAC`)
|
||||
}
|
||||
)
|
||||
|
||||
command(
|
||||
['!squad', '!sq'],
|
||||
'Buy upgrades that help the whole team.\n' +
|
||||
|
@ -1509,7 +1559,7 @@ command(
|
|||
const humanMembers = members.filter(name => name.length === 11)
|
||||
return say(`Hvacker owns ${humanMembers.length} souls.`)
|
||||
}
|
||||
const user = getUser(targetId)
|
||||
const user = await getUser(targetId)
|
||||
if (user.isDisabled) {
|
||||
return say(`<@${targetId}> is no longer with us.`)
|
||||
}
|
||||
|
@ -1524,6 +1574,7 @@ command(
|
|||
'Donate coins to a fellow player\n' +
|
||||
' Send coins by saying \'!gift @player coin_amount\'',
|
||||
async ({ event, args, say, user, haunted }) => {
|
||||
return say(`I'm sorry, but you people can't be trusted anymore.`)
|
||||
if (haunted) {
|
||||
return say(`!give doesn't work while you're haunted.`)
|
||||
}
|
||||
|
@ -1559,7 +1610,7 @@ command(
|
|||
}
|
||||
let gifted = []
|
||||
for (const targetId of targets) {
|
||||
const targetUser = getUser(targetId)
|
||||
const targetUser = await getUser(targetId)
|
||||
user.coins -= individualAmount
|
||||
if (user.coinsAllTime < 10000) {
|
||||
// return say('Let \'em play for a bit, ay?')
|
||||
|
@ -1696,14 +1747,15 @@ command(
|
|||
})
|
||||
|
||||
const strike = user => user.isDisabled ? '~' : ''
|
||||
const struck = (user, string) => strike(user) + string + strike(user)
|
||||
|
||||
const generateLeaderboard = ({ args }) => {
|
||||
const generateLeaderboard = ({ args }, showCat) => {
|
||||
let index = 1
|
||||
return Object.entries(users)
|
||||
.filter(([, user]) => (!user.isDisabled || args[0] === 'all') && (Object.entries(user.items).length > 0 || user.prestige))
|
||||
.filter(([, user]) => (!user.isDisabled || args.includes('all')) && user.name && !['Hvacker', '???', 'TEST-USER'].includes(user.name))
|
||||
.sort(([id, user1], [id2, user2]) => {
|
||||
const leftPrestige = getUser(id).prestige
|
||||
const rightPrestige = getUser(id2).prestige
|
||||
const leftPrestige = getUserSync(id).prestige
|
||||
const rightPrestige = getUserSync(id2).prestige
|
||||
if (leftPrestige > rightPrestige) {
|
||||
return -1
|
||||
}
|
||||
|
@ -1712,25 +1764,26 @@ const generateLeaderboard = ({ args }) => {
|
|||
}
|
||||
return getCPS(user1) > getCPS(user2)
|
||||
})
|
||||
.map(([id, u]) => `${strike(u)}${index++}. ${slack.users[id] || '???'} ${prestigeEmoji(u) || '-'} ${commas(getCPS(getUser(id)))} CPS - ${commas(getCoins(id))} HVAC${strike(u)}`)
|
||||
.map(([id, u]) => `${strike(u)}${index++}. ${slack.users[id] || '???'} ${prestigeEmoji(u) || '-'} ${commas(getCPS(getUserSync(id)))} CPS - ${commas(getCoins(id))} HVAC${showCat ? ` (${commas(users[id].coinsAllTime)} all-time)` : ''}${strike(u)}`)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
command(
|
||||
['!leaderboard', '!lb'],
|
||||
'Show the top HVAC-earners, ranked by prestige, then CPS',
|
||||
async ({ say, user, args }) => {
|
||||
async ({ say, user, args, event }) => {
|
||||
// if ((event.user === slack.users.Houston || event.user === slack.users.Admin) && event.channel_type.includes('im')) {
|
||||
// return say('```' + `Hvacker - 9 souls - Taking from them whatever it desires\nSome other losers - who cares - whatever` + '```')
|
||||
// }
|
||||
game.leaderboardChannels ??= {}
|
||||
await say(generateLeaderboard({ args })).then(({ channel, ts }) => {
|
||||
addAchievement(user, 'leaderBoardViewer', say)
|
||||
if (args[0] === 'all') {
|
||||
const showCat = event.user === slack.users.Admin && args.includes('cat')
|
||||
await say(generateLeaderboard({ args }, showCat)).then(({ channel, ts }) => {
|
||||
addAchievement(user, 'leaderBoardViewer', say)
|
||||
if (args.includes('all') || showCat) {
|
||||
return
|
||||
}
|
||||
game.leaderboardChannels[channel] = ts
|
||||
return updateAllLeaderboards({ channel, ts })
|
||||
game.leaderboardChannels[channel] = ts
|
||||
return updateAllLeaderboards({ channel, ts })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
@ -1751,7 +1804,20 @@ const oneShot = (name, helpText, message, achievementName) => {
|
|||
|
||||
command(names, helpText, async ({ say, user }) => {
|
||||
await say(message)
|
||||
await slack.messageAdmin(`Wow buddy they like your ${name} joke.`)
|
||||
if (achievementName) {
|
||||
addAchievement(user, achievementName, say)
|
||||
}
|
||||
}, { hidden: true })
|
||||
}
|
||||
|
||||
const oneShotFile = async (name, helpText, text, filePath, achievementName) => {
|
||||
const names = Array.isArray(name) ? name : [name]
|
||||
command(names, helpText, async ({ event }) => {
|
||||
await slack.app.client.files.upload({
|
||||
channels: event.channel,
|
||||
initial_comment: text,
|
||||
file: createReadStream(filePath)
|
||||
})
|
||||
if (achievementName) {
|
||||
addAchievement(user, achievementName, say)
|
||||
}
|
||||
|
@ -1759,15 +1825,16 @@ const oneShot = (name, helpText, message, achievementName) => {
|
|||
}
|
||||
|
||||
oneShot('!peter-griffin-family-guy', 'Good stuff great comedy.', `Are you sure?\nThis will permanently delete all of your progress and remove you from the game.\nSay !!peter-griffin-family-guy to confirm.`)
|
||||
oneShot('!santa', 'Ho ho ho!', '<https://i.imgur.com/dBWgFfQ.png|I\'m Santa Quacks>')
|
||||
oneShotFile('!santa', 'Ho ho ho!', '<https://i.imgur.com/dBWgFfQ.png|I\'m Santa Quacks>')
|
||||
oneShot('!sugma', 'Not very original.', ':hvacker_angery:')
|
||||
oneShot('!pog', 'One poggers hvacker', '<https://i.imgur.com/XCg7WDz.png|poggers>')
|
||||
oneShot('!ligma', 'Not very original.', '<https://i.imgur.com/i1YtW7m.png|no>')
|
||||
oneShot(['!dab', '!dabs'], 'ACTIVATE COOL GUY MODE', '<https://i.imgur.com/FKYdeqo.jpg|I go XD style>', 'certifiedCoolGuy')
|
||||
oneShot('!based', 'Sorry, it\'s a little hard to hear you!', '<https://i.imgur.com/IUX6R26.png|What?>')
|
||||
oneShot('!shrek', 'Is love and is life.', '<https://i.imgur.com/QwuCQZA.png|Donkey!>')
|
||||
oneShot('!sugondese', 'I don\'t like you.', '<https://i.imgur.com/VCvfvdz.png|rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr>')
|
||||
oneShotFile('!pog', 'One poggers hvacker', 'poggers', 'images/XCg7WDz.png')
|
||||
oneShotFile('!ligma', 'Not very original.', 'no', 'images/i1YtW7m.png')
|
||||
oneShotFile(['!dab', '!dabs'], 'ACTIVATE COOL GUY MODE', 'I go XD style', 'images/FKYdeqo.jpg', 'certifiedCoolGuy')
|
||||
oneShotFile('!based', 'Sorry, it\'s a little hard to hear you!', 'What?', 'images/IUX6R26.png')
|
||||
oneShotFile('!shrek', 'Is love and is life.', 'Donkey!', 'images/QwuCQZA.png')
|
||||
oneShotFile('!sugondese', 'I don\'t like you.', 'rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr', 'images/VCvfvdz.png')
|
||||
oneShot('!bofa', 'bofa deez yes yes we get it', ':goorab:')
|
||||
oneShotFile('!squeedward', 'Who lives in an easter island head, under the sea?', 'Squeedward', 'images/squeedward.png')
|
||||
// oneShot('!imaginedragons', 'The worst part about any group of white people is that they could be Imagine Dragons and you\'d have no way of knowing.', '<https://i.imgur.com/QwuCQZA.png|Donkey!>')
|
||||
|
||||
command(
|
||||
|
@ -1778,9 +1845,9 @@ command(
|
|||
|
||||
command(
|
||||
['!addnft'],
|
||||
'Arguments 1 and 2 should be on the first line as name (one word!) and integer price.\n' +
|
||||
' The second line should be the description of the pieces\n' +
|
||||
' the picture is everything after the second line',
|
||||
`Arguments 1 and 2 should be on the first line as name (one word!) and integer price.
|
||||
The second line should be the description of the pieces
|
||||
the picture is everything after the second line`,
|
||||
async ({ event }) => {
|
||||
let [, name, ...price] = event.text.substring(0, event.text.indexOf('\n')).split(' ')
|
||||
const rest = event.text.substring(event.text.indexOf('\n') + 1)
|
||||
|
@ -1852,7 +1919,7 @@ command(
|
|||
async ({ args, say }) => {
|
||||
const target = args[0]
|
||||
const targetId = idFromWord(target)
|
||||
const targetUser = getUser(targetId)
|
||||
const targetUser = await getUser(targetId)
|
||||
await say(
|
||||
`${target} is currently earning \`${commas(getCPS(targetUser))}\` HVAC Coin per second.\n\n` +
|
||||
`They have ${commas(getCoins(targetId))} HVAC Coins\n\n` +
|
||||
|
@ -1881,7 +1948,7 @@ command(
|
|||
if (user.coins < amount) {
|
||||
return
|
||||
}
|
||||
const targetUser = getUser(targetId)
|
||||
const targetUser = await getUser(targetId)
|
||||
user.coins -= amount
|
||||
targetUser.coins += amount
|
||||
await say(`Stealing is wrong. Gave ${commas(amount)} of your HVAC to ${slack.users[targetId]}`)
|
||||
|
@ -1920,7 +1987,7 @@ command(
|
|||
['!giveach'],
|
||||
'!giveach @player ach_name',
|
||||
async ({ args, say, }) => {
|
||||
addAchievement(getUser(idFromWord(args[0])), args[1], say)
|
||||
addAchievement(await getUser(idFromWord(args[0])), args[1], say)
|
||||
//saveGame()
|
||||
}, adminOnly)
|
||||
|
||||
|
@ -2202,7 +2269,7 @@ command(
|
|||
command(
|
||||
['!userjson'],
|
||||
'Fetch the raw JSON data for your user',
|
||||
async ({ user, trueSay }) => trueSay(JSON.stringify(user)),
|
||||
async ({ user, trueSay }) => trueSay('```\n' + JSON.stringify(user) + '\n```'),
|
||||
{ hidden: true }
|
||||
)
|
||||
|
||||
|
@ -2261,6 +2328,7 @@ command(
|
|||
)
|
||||
|
||||
webapi.launch()
|
||||
logMemoryUsage()
|
||||
|
||||
module.exports = {
|
||||
command,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
const { addAchievement, saveGame, getUser } = require('./utils')
|
||||
const { addAchievement, saveGame, getUser, saveUser } = require('./utils')
|
||||
const slack = require('../../slack')
|
||||
|
||||
let loreCount = 0
|
||||
const l = (text, {correctReactions, correctResponse, incorrectResponse, jumpTo} = {}) => {
|
||||
const l = (text, {file, correctReactions, correctResponse, incorrectResponse, jumpTo} = {}) => {
|
||||
loreCount += 1
|
||||
return {
|
||||
text,
|
||||
file,
|
||||
correctReactions,
|
||||
correctResponse,
|
||||
incorrectResponse,
|
||||
|
@ -37,13 +38,12 @@ const lore = [
|
|||
l(`The seventh was Small Bob.`),
|
||||
l(`The eighth was Maurice, who had quite a few words to say about the French, and was eventually elected president.`),
|
||||
l(`And the ninth...`),
|
||||
l(`Well, the ninth might actually amount to something.`),
|
||||
l(`https://i.imgur.com/eFreg7Y.gif\n`),
|
||||
l(`Well, the ninth might actually amount to something.`, {file: 'https://i.imgur.com/eFreg7Y.gif'})
|
||||
]
|
||||
|
||||
slack.onReaction(async ({ event, say }) => {
|
||||
try {
|
||||
const user = getUser(event.user)
|
||||
const user = await getUser(event.user)
|
||||
const item = await slack.getMessage({ channel: event.item.channel, ts: event.item.ts })
|
||||
const message = item.messages[0].text
|
||||
const loreData = slack.decodeData('lore', message)
|
||||
|
@ -59,7 +59,7 @@ slack.onReaction(async ({ event, say }) => {
|
|||
console.log('lore:', lore[user.lore])
|
||||
await say(lore[user.lore].correctResponse)
|
||||
user.lore += 1
|
||||
saveGame(`updating ${user.name}'s lore counter`)
|
||||
saveUser(event.user, user, 'updating lore counter')
|
||||
} catch (e) {console.error('onReaction error', e)}
|
||||
})
|
||||
|
||||
|
@ -77,7 +77,7 @@ const loreMessage = (user, say) => {
|
|||
return `Sorry. I'd love to tell you more, but I'm tired. Please check back later.`
|
||||
}
|
||||
|
||||
const loreRoute = async ({ say, args, user, isAdmin }) => {
|
||||
const loreRoute = async ({ say, args, user, isAdmin, event }) => {
|
||||
user.lore ??= 0
|
||||
if (!args[0]) {
|
||||
const message = loreMessage(user, say)
|
||||
|
@ -92,6 +92,7 @@ const loreRoute = async ({ say, args, user, isAdmin }) => {
|
|||
if (args[0] === 'reset') {
|
||||
user.lore = 0
|
||||
//saveGame()
|
||||
await saveUser(event.user, user, 'lore reset')
|
||||
return say(`I have reset your place in the story.`)
|
||||
}
|
||||
if (isAdmin) {
|
||||
|
@ -105,6 +106,7 @@ const loreRoute = async ({ say, args, user, isAdmin }) => {
|
|||
const jumpTo = parseInt(args[0])
|
||||
if (!isNaN(jumpTo)) {
|
||||
user.lore = jumpTo
|
||||
await saveUser(event.user, user, 'lore jump')
|
||||
//saveGame()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { commas, quackGradeMultiplier, prestigeMultiplier, makeBackup, userHasCheckedQuackgrade, getUser } = require('./utils')
|
||||
const { commas, quackGradeMultiplier, prestigeMultiplier, makeBackup, userHasCheckedQuackgrade, getUser, saveUser } = require('./utils')
|
||||
const { quackStore } = require('./quackstore')
|
||||
const buyableItems = require('./buyableItems')
|
||||
const slack = require('../../slack')
|
||||
|
@ -77,6 +77,7 @@ const prestigeConfirmRoute = async ({ event, say, user, YEET }) => {
|
|||
|
||||
await say(prestigeMenu(user))
|
||||
await say(`Say !quack _upgrade-name_ to purchase new quackgrades!`)
|
||||
await saveUser(event.user, user, 'prestiging')
|
||||
//await say('You prestiged! Check out !quackstore to see what you can buy!')
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ const quackStoreText = user =>
|
|||
`\n\nYou have ${user.quacks ??= 0} quacks to spend.` +
|
||||
`\nQuackStore upgrades are currently boosting your CPS by ${commas((quackGradeMultiplier(user) - 1) * 100)}%`
|
||||
|
||||
const quackStoreRoute = async ({ user, say, args, YEET }) => {
|
||||
const quackStoreRoute = async ({ user, say, args, YEET, event }) => {
|
||||
user.quackUpgrades ??= {}
|
||||
if (!args[0] || !YEET) {
|
||||
await say(quackStoreText(user))
|
||||
|
@ -131,7 +132,9 @@ const quackStoreRoute = async ({ user, say, args, YEET }) => {
|
|||
if (quackItem.type === 'starter') {
|
||||
quackItem.effect(user)
|
||||
}
|
||||
|
||||
await say(`You bought ${args[0]}!`)
|
||||
await saveUser(event.user, user, 'quackgrade purchased')
|
||||
}
|
||||
|
||||
const buyQuackGradeButton = quackgrade => {
|
||||
|
@ -237,7 +240,7 @@ const buyQuackGrade = async ({ body, ack, say, trueSay, payload }) => {
|
|||
const buying = payload.action_id.substring('buy-quackgrade-'.length)
|
||||
|
||||
console.log(`buyQuackGrade ${buying} clicked`)
|
||||
const user = getUser(body.user.id)
|
||||
const user = await getUser(body.user.id)
|
||||
// if (!user.isPrestiging) {
|
||||
// console.log('You must be prestiging!')
|
||||
// return say(`You must be prestiging to use this menu!`)
|
||||
|
@ -262,7 +265,7 @@ Object.keys(quackStore).forEach(itemName => slack.app.action('buy-quackgrade-' +
|
|||
slack.app.action('complete_prestige', async ({ body, ack, say }) => {
|
||||
await ack()
|
||||
|
||||
const user = getUser(body.user.id)
|
||||
const user = await getUser(body.user.id)
|
||||
delete user.isPrestiging
|
||||
|
||||
await slack.app.client.chat.delete({
|
||||
|
@ -271,6 +274,7 @@ slack.app.action('complete_prestige', async ({ body, ack, say }) => {
|
|||
})
|
||||
|
||||
await say(`Prestige complete!`)
|
||||
await saveUser(body.user.id, user, 'prestige complete')
|
||||
})
|
||||
|
||||
const prestigeMenuRoute = async ({ say, user }) => {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
const { Pool } = require('pg')
|
||||
const fs = require('fs')
|
||||
const achievements = require('./achievements')
|
||||
const buyableItems = require('./buyableItems')
|
||||
const { quackStore, getChaos } = require('./quackstore')
|
||||
|
||||
const dbPool = new Pool()
|
||||
|
||||
let jokes
|
||||
let slackUsers
|
||||
const setSlackUsers = users => {
|
||||
|
@ -64,18 +67,38 @@ const makeBackup = () => {
|
|||
fs.writeFileSync(fileName, JSON.stringify(game))
|
||||
}
|
||||
|
||||
const saveUser = async (userId, user, after) => {
|
||||
return
|
||||
const name = user.name || userId
|
||||
if (after) {
|
||||
console.log(`SAVING ${name} after ${after}`)
|
||||
} else {
|
||||
console.log(`SAVING ${name}`, user)
|
||||
}
|
||||
await dbPool.query(`
|
||||
INSERT INTO hvacker_user (slack_id, name, data)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (slack_id) DO UPDATE
|
||||
SET data = EXCLUDED.data`
|
||||
, [userId, user.name, user])
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
let saves = 0
|
||||
const saveGame = (after, force = true) => {
|
||||
const saveGame = (after, force = true, skipLog = false) => {
|
||||
if (saves % 20 === 0) {
|
||||
makeBackup()
|
||||
}
|
||||
saves += 1
|
||||
if (force || saves % 10 === 0) {
|
||||
if (after) {
|
||||
console.log(`SAVING GAME after ${after}`)
|
||||
} else {
|
||||
console.log('SAVING GAME')
|
||||
if (!skipLog) {
|
||||
if (after) {
|
||||
console.log(`SAVING GAME after ${after}`)
|
||||
} else {
|
||||
console.log('SAVING GAME')
|
||||
}
|
||||
}
|
||||
// Object.entries(game.users).forEach(([userId, user]) => saveUser(userId, user))
|
||||
|
||||
fs.writeFileSync(saveDir + saveFile, JSON.stringify(game, null, 2))
|
||||
}
|
||||
|
@ -217,7 +240,19 @@ const calculateCost = ({ itemName, user, quantity = 1 }) => {
|
|||
}
|
||||
|
||||
const game = loadGame()
|
||||
const { users, nfts, squad } = game
|
||||
let { users, nfts, squad } = game
|
||||
|
||||
const getAllUsers = async () => {
|
||||
const result = await dbPool.query(`
|
||||
SELECT slack_id, data
|
||||
FROM hvacker_user
|
||||
`).catch(console.error)
|
||||
return Object.fromEntries(result.rows.map(
|
||||
({ slack_id: slackId, data }) => [slackId, data]))
|
||||
}
|
||||
// getAllUsers().then(collection => {
|
||||
// game.users = (users = collection)
|
||||
// })
|
||||
|
||||
const setHighestCoins = userId => {
|
||||
const prevMax = users[userId].highestEver || 0
|
||||
|
@ -253,7 +288,24 @@ const getIdFromName = name => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const getUser = (userId, updateCoins = false) => {
|
||||
const fetchUser = async (userId, updateCoins = false) => {
|
||||
const result = await dbPool.query(`
|
||||
SELECT data
|
||||
FROM hvacker_user
|
||||
WHERE slack_id = $1`
|
||||
, [userId])
|
||||
.catch(console.error)
|
||||
return result.rows[0]?.data
|
||||
}
|
||||
|
||||
const getUser = async (userId, updateCoins = false) => {
|
||||
// users[userId] = await fetchUser(userId)
|
||||
// console.log('USER', users[userId])
|
||||
//users[userId] = await fetchUser(userId)
|
||||
return getUserSync(userId, updateCoins)
|
||||
}
|
||||
|
||||
const getUserSync = (userId, updateCoins = false) => {
|
||||
users[userId] ??= {}
|
||||
users[userId].coins ??= 0
|
||||
users[userId].items ??= {}
|
||||
|
@ -264,8 +316,9 @@ const getUser = (userId, updateCoins = false) => {
|
|||
users[userId].startDate ??= new Date()
|
||||
// users[userId].name ??= slack.users[userId]
|
||||
if (updateCoins) {
|
||||
users[userId].coins = getCoins(userId)
|
||||
users[userId].coins = getCoins(userId, users[userId])
|
||||
}
|
||||
saveGame('getUserSync()', true, true)
|
||||
return users[userId]
|
||||
}
|
||||
|
||||
|
@ -276,8 +329,8 @@ const addCoins = (user, add) => {
|
|||
user.coins = Math.floor(user.coins)
|
||||
}
|
||||
|
||||
const getCoins = userId => {
|
||||
const user = getUser(userId)
|
||||
const getCoins = (userId, user) => {
|
||||
user = user || getUserSync(userId)
|
||||
const currentTime = getSeconds()
|
||||
const lastCheck = user.lastCheck || currentTime
|
||||
const secondsPassed = currentTime - lastCheck
|
||||
|
@ -577,8 +630,26 @@ const updateAll = async ({ name, text, blocks, add: { channel, ts } = {} }) => {
|
|||
// // return alreadyHas
|
||||
}
|
||||
|
||||
const logMemoryUsage = name => {
|
||||
const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
|
||||
|
||||
const memoryData = process.memoryUsage();
|
||||
const formattedData = {
|
||||
rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`,
|
||||
// heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`,
|
||||
// heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`,
|
||||
// external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`,
|
||||
}
|
||||
if (name) {
|
||||
console.log(name, formattedData)
|
||||
} else {
|
||||
console.log(formattedData)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
saveGame,
|
||||
saveUser,
|
||||
makeBackup,
|
||||
logError,
|
||||
parseOr,
|
||||
|
@ -589,6 +660,7 @@ module.exports = {
|
|||
addAchievement,
|
||||
getCoins,
|
||||
getUser,
|
||||
getUserSync,
|
||||
singleItemCps,
|
||||
getCPS,
|
||||
getItemCps,
|
||||
|
@ -617,5 +689,6 @@ module.exports = {
|
|||
setSlackAppClientChatUpdate: update => slackAppClientChatUpdate = update,
|
||||
setUpgrades,
|
||||
setSlackUsers,
|
||||
setJokes: _jokes => jokes = _jokes
|
||||
setJokes: _jokes => jokes = _jokes,
|
||||
logMemoryUsage
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const express = require('express')
|
||||
const app = express()
|
||||
const port = 3001
|
||||
const port = process.env.HVACKER_API_PORT || 3001
|
||||
const crypto = require('crypto')
|
||||
const base64 = require('base-64')
|
||||
const slack = require('../../slack')
|
||||
const { game: { users }, getUser, fuzzyMatcher } = require('./utils')
|
||||
const { game: { users }, getUser, fuzzyMatcher, saveUser } = require('./utils')
|
||||
|
||||
const apiGetUserId = hash => {
|
||||
return Object.entries(users)
|
||||
|
@ -71,35 +71,21 @@ const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
|
|||
const encoded = req.header('Authorization').substring(5)
|
||||
const decoded = base64.decode(encoded).substring(1)
|
||||
const hash = makeHash(decoded)
|
||||
console.log({ hash })
|
||||
|
||||
const event = {
|
||||
user: apiGetUserId(hash)
|
||||
}
|
||||
const user = getUser(event.user)
|
||||
const user = await getUser(event.user)
|
||||
if (user.name !== 'TEST-USER' && illegalCommands.includes(commandName?.toLowerCase())) {
|
||||
res.send('Command is illegal over the web api!')
|
||||
return
|
||||
}
|
||||
if (!event.user) {
|
||||
res.status(400)
|
||||
res.send(
|
||||
'User with that password does not exist, or user does not have a password.\n' +
|
||||
'See \'!setpw help\' for assistance.\n'
|
||||
)
|
||||
console.log(' bad password')
|
||||
res.send(`User with that password does not exist, or user does not have a password.
|
||||
See '!setpw help' for assistance.\n`)
|
||||
return
|
||||
}
|
||||
// const lastCall = lastCalls[event.user] || 0
|
||||
// const secondsBetweenCalls = 2
|
||||
// const currentTime = Math.floor(new Date().getTime() / 1000)
|
||||
// if (lastCall + secondsBetweenCalls > currentTime) {
|
||||
// res.status(400)
|
||||
// res.send(`Must have at least ${secondsBetweenCalls}s between api calls`)
|
||||
// console.log(' rate limited')
|
||||
// return
|
||||
// }
|
||||
// console.log(` went through for ${slack.users[event.user]}`)
|
||||
// lastCalls[event.user] = currentTime
|
||||
|
||||
if (words[1] === 'help') {
|
||||
await say(commandNames.map(name => `\`${name}\``).join(', ') + ': ' + helpText)
|
||||
|
@ -109,19 +95,19 @@ const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
|
|||
return
|
||||
}
|
||||
const haunted = false
|
||||
//await action({ event, say, words, args, commandName })
|
||||
const canUse = await condition({ event, say, words, commandName, args, user, userId: event.user, isAdmin: event.user.includes(slack.users.Admin) })
|
||||
if (!canUse) {
|
||||
await say(`Command '${words[0]}' not found`)
|
||||
return
|
||||
}
|
||||
await action({ event, say, trueSay: say, words, args, commandName, user, userId: event.user, haunted })
|
||||
await saveUser(event.user, user, 'webapi action')
|
||||
} catch (e) {
|
||||
console.error('route error', e)
|
||||
const example = "`curl --location-trusted -u ':your-pw' quacker.sagev.space/lb`"
|
||||
await say(`Routing error. Make sure you've set up API access with the !setpw command in slack!\n` +
|
||||
`Then you can use calls like ${example}\n` +
|
||||
`N.b. --location-trusted is needed because quacker.sagev.space is technically a redirect, and your headers need to be forwarded.`)
|
||||
await say(`Routing error. Make sure you've set up API access with the !setpw command in slack!
|
||||
Then you can use calls like ${example}
|
||||
N.b. --location-trusted is needed because quacker.sagev.space is technically a redirect, and your headers need to be forwarded.`)
|
||||
}
|
||||
}
|
||||
commandNames.forEach(name => name !== '!!help' &&
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const { App: SlackApp } = require('@slack/bolt')
|
||||
const config = require('../config')
|
||||
const fs = require('fs')
|
||||
const http = require('http')
|
||||
const { addReactions, saveGame, setSlackAppClientChatUpdate, parseOr, setSlackUsers } = require('../games/hvacoins/utils')
|
||||
|
||||
const temperatureChannelId = 'C034156CE03'
|
||||
|
@ -82,6 +83,9 @@ const activePolls = {}
|
|||
const testId = 'U028BMEBWBV_TEST'
|
||||
let testMode = false
|
||||
app.event('message', async ({ event, context, client, say }) => {
|
||||
if (event.text?.startsWith('!!!')) {
|
||||
return
|
||||
}
|
||||
if (event.subtype !== 'message_changed' && event?.text !== '!') {
|
||||
console.log('message.event', {
|
||||
...event,
|
||||
|
@ -89,6 +93,16 @@ app.event('message', async ({ event, context, client, say }) => {
|
|||
})
|
||||
}
|
||||
if (event?.user === users.Admin) {
|
||||
if (event.files?.length) {
|
||||
const fileName = Math.random().toString()
|
||||
const file = fs.createWriteStream(fileName)
|
||||
await app.client.files.upload({
|
||||
channels: event.channel,
|
||||
initial_comment: `Here's your file!`,
|
||||
file: createReadStream(path)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (event?.text.startsWith('!')) {
|
||||
if (testMode) {
|
||||
await messageAdmin('Currently in test mode!')
|
||||
|
@ -215,11 +229,19 @@ app.event('message', async ({ event, context, client, say }) => {
|
|||
|
||||
let text
|
||||
if (hotterVotes > colderVotes && hotterVotes > contentVotes) {
|
||||
text = `<@${users[users.ThermoController]}> The people have spoken, and would like to `
|
||||
if (users.ThermoController === 'someone with hands') {
|
||||
text = `@SomeoneWithHands, The people have spoken, and would like to `
|
||||
} else {
|
||||
text = `<@${users[users.ThermoController]}> The people have spoken, and would like to `
|
||||
}
|
||||
text += 'raise the temperature, quack.'
|
||||
requestTempChange('Hotter')
|
||||
} else if (colderVotes > hotterVotes && colderVotes > contentVotes) {
|
||||
text = `<@${users[users.ThermoController]}> The people have spoken, and would like to `
|
||||
if (users.ThermoController === 'someone with hands') {
|
||||
text = `@SomeoneWithHands, The people have spoken, and would like to `
|
||||
} else {
|
||||
text = `<@${users[users.ThermoController]}> The people have spoken, and would like to `
|
||||
}
|
||||
text += 'lower the temperature, quack quack.'
|
||||
requestTempChange('Colder')
|
||||
} else {
|
||||
|
|