Skip to content

Commit

Permalink
Add faucet bot filter (#13)
Browse files Browse the repository at this point in the history
* WIP: Add faucet bot filter

* fix: Refactor the code.

* fix: update Readme.md to explain the REDIS_URL environment variable.

* fix: Move requestFilter from clients to middlewares.
fix: Add cooldown time

* fix: Fix the style

* add prettier check script

* add style check action

* format code

Co-authored-by: Hoon Kim <[email protected]>
  • Loading branch information
y-shijo and hoonsubin authored Nov 9, 2021
1 parent 1f7a198 commit 25d91be
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 33 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/pr_action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ jobs:
yarn
yarn test
working-directory: ./
run_style_check:
name: Code Style Check
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: check_style
run: |
yarn
yarn lint:check
working-directory: ./
build_code:
name: Build Check
runs-on: ubuntu-latest
Expand Down
10 changes: 5 additions & 5 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
semi: true,
trailingComma: "all",
singleQuote: true,
printWidth: 120,
tabWidth: 4,
semi: true,
trailingComma: 'all',
singleQuote: true,
printWidth: 120,
tabWidth: 4,
};
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,22 @@ You can do this by creating a `.env` file with the following variables.
```env
# Bot user app token
DISCORD_APP_TOKEN=<bot token>
# Bot user client ID
DISCORD_APP_CLIENT_ID=<app id>
# Server ID for the bot to be installed
DISCORD_GUILD_ID=<guild id>
# The channel ID for the bot to listen to
DISCORD_FAUCET_CHANNEL_ID=<channel id>
# Secret phrase (mnemonic) for the faucet account
FAUCET_SECRET_PHRASE=<secret phrase>
# Redis URL in URL format
# In Heroku, this is automatically set when you add the Redis add-on to your app.
REDIS_URL=<redis://[:password@]host[:port][/db-number][?option=value]>
```

### Scripts
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"serve": "node build/index.js",
"dev": "ts-node-dev -r dotenv/config src/index.ts",
"build": "tsc --project tsconfig.json",
"lint": "eslint '*/**/*.{js,ts}' --quiet --fix",
"lint:check": "eslint '*/**/*.{js,ts}'",
"lint": "eslint '*/**/*.{js,ts}' --quiet --fix && prettier -w .",
"lint:check": "eslint '*/**/*.{js,ts}' && prettier -c .",
"test": "NODE_ENV=test echo \"Test not implemented\"!"
},
"engines": {
Expand All @@ -37,6 +37,7 @@
"license": "MIT",
"devDependencies": {
"@types/express": "^4.17.13",
"@types/ioredis": "^4.28.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.9.3",
"@types/node-fetch": "2.5.10",
Expand All @@ -63,6 +64,7 @@
"@polkadot/util-crypto": "^7.4.1",
"discord-api-types": "^0.23.1",
"discord.js": "^13.1.0",
"express": "^4.17.1"
"express": "^4.17.1",
"ioredis": "^4.28.0"
}
}
13 changes: 12 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
NetworkName,
ASTAR_TOKEN_DECIMALS,
} from './clients';
import { canRequestFaucet, logRequest } from './middlewares';

import { DISCORD_APP_TOKEN, DISCORD_APP_CLIENT_ID, DISCORD_GUILD_ID, DISCORD_FAUCET_CHANNEL_ID } from './config';
import { Client, Intents, Interaction } from 'discord.js';
import BN from 'bn.js';
Expand Down Expand Up @@ -73,9 +75,15 @@ const discordFaucetApp = async (appCred: DiscordCredentials) => {
throw new Error('No address was given!');
}

// todo: check if the user has already requested tokens or not
// Send 'Waiting' message to the user
await interaction.deferReply();

// Check if the user has already requested tokens or not
const requesterId = interaction.user.id;
const now = Date.now();
await canRequestFaucet(requesterId, now);

// Send token to the requester
console.log(`Sending ${astarApi.formatBalance(dripAmount)} to ${address}`);

await astarApi.sendTokenTo(address);
Expand All @@ -87,6 +95,9 @@ const discordFaucetApp = async (appCred: DiscordCredentials) => {
astarApi.faucetAccount.address
}\``,
);

// Log the faucet request.
await logRequest(requesterId, now);
} catch (err) {
console.warn(err);
await interaction.editReply({ content: `${err}` });
Expand Down
52 changes: 28 additions & 24 deletions src/config/appConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,33 @@
"discord": {
"scope": ["bot", "applications.commands"],
"permissions": 2147551232,
"slashCommands": [{
"name": "drip",
"type": 1,
"description": "Receive a testnet token from the faucet",
"options": [{
"name": "network",
"description": "The name of the testnet",
"type": 3,
"required": true,
"choices": [{
"name": "Shibuya",
"value": "shibuya"
}]
},
{
"name": "address",
"description": "Your SS58 (Substrate) or H160 (EVM) public address for receiving the token",
"type": 3,
"required": true
}
]
}]
"slashCommands": [
{
"name": "drip",
"type": 1,
"description": "Receive a testnet token from the faucet",
"options": [
{
"name": "network",
"description": "The name of the testnet",
"type": 3,
"required": true,
"choices": [
{
"name": "Shibuya",
"value": "shibuya"
}
]
},
{
"name": "address",
"description": "Your SS58 (Substrate) or H160 (EVM) public address for receiving the token",
"type": 3,
"required": true
}
]
}
]
},

"network": {
Expand Down Expand Up @@ -137,8 +142,7 @@
"rewards": "Balance",
"staked": "Balance"
}

}
}
}
}
}
1 change: 1 addition & 0 deletions src/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './requestFilter';
28 changes: 28 additions & 0 deletions src/middlewares/requestFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);

// Cooldown time in millisecond.
// The requester must wait for Cooldown time to request next faucet.
const cooldownTimeMillisecond = 60 * 60 * 1000;

// Check whether the requester can request Faucet or not based on the last request time.
export const canRequestFaucet = async (requesterId: string, now: number): Promise<void> => {
const lastRequestAt = Number(await redis.get(requesterId));
const elapsedTimeFromLastRequest = now - lastRequestAt;

// If lastReuqest was made within the cooldown time, the requester cannot request.
if (cooldownTimeMillisecond > elapsedTimeFromLastRequest) {
const untilNextRequestMillisec = cooldownTimeMillisecond - elapsedTimeFromLastRequest;
const untilNextRequestMin = Math.floor(untilNextRequestMillisec / 1000 / 60);
const untilNextRequestSec = Math.floor(untilNextRequestMillisec / 1000 - untilNextRequestMin * 60);

const replyMessage = `You already requested the Faucet. Try again in ${untilNextRequestMin} mins ${untilNextRequestSec} secs.`;
throw new Error(replyMessage);
}
};

// Log the Faucet request on Redis
export const logRequest = async (requesterId: string, now: number): Promise<void> => {
await redis.set(requesterId, now, 'PX', cooldownTimeMillisecond);
};
76 changes: 76 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,13 @@
dependencies:
"@types/node" "*"

"@types/ioredis@^4.28.1":
version "4.28.1"
resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.1.tgz#27d66f4c0540145826d984b6d0a5b54bbb88c32a"
integrity sha512-raYHPqRWrfnEoym94BY28mG1+tcZqh3dsp2q7x5IyMAAEvIdu+H0X8diASMpncIm+oHyH9dalOeOnGOL/YnuOA==
dependencies:
"@types/node" "*"

"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
Expand Down Expand Up @@ -1571,6 +1578,11 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"

cluster-key-slot@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==

co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
Expand Down Expand Up @@ -1754,6 +1766,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=

denque@^1.1.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf"
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==

depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
Expand Down Expand Up @@ -2550,6 +2567,23 @@ [email protected]:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=

ioredis@^4.28.0:
version "4.28.0"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.0.tgz#5a2be3f37ff2075e2332f280eaeb02ab4d9ff0d3"
integrity sha512-I+zkeeWp3XFgPT2CtJKxvaF5FjGBGt4yGYljRjQecdQKteThuAsKqffeF1lgHVlYnuNeozRbPOCDNZ7tDWPeig==
dependencies:
cluster-key-slot "^1.1.0"
debug "^4.3.1"
denque "^1.1.0"
lodash.defaults "^4.2.0"
lodash.flatten "^4.4.0"
lodash.isarguments "^3.1.0"
p-map "^2.1.0"
redis-commands "1.7.0"
redis-errors "^1.2.0"
redis-parser "^3.0.0"
standard-as-callback "^2.1.0"

ip-regex@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5"
Expand Down Expand Up @@ -3218,6 +3252,21 @@ lodash.clonedeep@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=

lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=

lodash.flatten@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=

lodash.isarguments@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=

lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
Expand Down Expand Up @@ -3525,6 +3574,11 @@ p-locate@^4.1.0:
dependencies:
p-limit "^2.2.0"

p-map@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==

p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
Expand Down Expand Up @@ -3710,6 +3764,23 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"

[email protected]:
version "1.7.0"
resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89"
integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==

redis-errors@^1.0.0, redis-errors@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=

redis-parser@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4"
integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=
dependencies:
redis-errors "^1.0.0"

regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
Expand Down Expand Up @@ -3948,6 +4019,11 @@ stack-utils@^2.0.3:
dependencies:
escape-string-regexp "^2.0.0"

standard-as-callback@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==

"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
Expand Down

0 comments on commit 25d91be

Please sign in to comment.