add: initial working poc

This commit is contained in:
Mohamed Sohail 2022-01-14 14:46:59 +03:00
commit 4372632d93
Signed by: kamikazechaser
GPG Key ID: 7DD45520C01CD85D
7 changed files with 290 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "ussd-tg-proxy",
"version": "0.0.1",
"description": "ussd-tg-proxy",
"main": "src/bot.js",
"repository": "https://git.grassecon.net/grassrootseconomics/ussd-tg-proxy.git",
"author": "Mohamed Sohail <sohalazim@pm.me>",
"license": "GPL-3",
"private": false,
"dependencies": {
"grammy": "^1.6.1",
"ioredis": "^4.28.3",
"phin": "^3.6.1"
}
}

70
src/bot.js Normal file
View File

@ -0,0 +1,70 @@
// std lib
const crypto = require("crypto");
// npm imports
const { Bot, Keyboard } = require("grammy");
// module imports
const cache = require("./cache");
const request = require("./request");
// TODO: get value from confini
// TODO: webhook support
const bot = new Bot("");
// TODO: handle errors
bot.command("start", async (ctx) => {
const tgLinked = await cache.get(ctx.msg.from.id);
if (tgLinked) {
await cache.set(tgLinked, crypto.randomBytes(16).toString("hex"));
const res = await request.proxy(tgLinked);
return ctx.reply(res.text);
}
const keyboard = new Keyboard();
return await ctx.reply(
"Click on the button below to link your phone number.",
{
reply_markup: keyboard.requestContact("Link Phone"),
}
);
});
// TODO: handle errors
bot.on("message:text", async (ctx) => {
const tgLinked = await cache.get(ctx.msg.from.id);
if (tgLinked) {
const res = await request.proxy(tgLinked, ctx.msg.text);
if (res.code !== "END") {
return ctx.reply(res.text);
}
return await cache.del(tgLinked);
}
return await ctx.reply(
"Session expired or account not linked. /start the bot again to access your Sarafu account."
);
});
// TODO: handle errors
bot.on("msg:contact", async (ctx) => {
// TODO: check if msg is reply from bot to prevent contact share bypass
const contact = ctx.msg.contact;
if (contact.phone_number.slice(0, 4) !== "+254") {
return ctx.reply("Sarafu is only available in Kenya at the moment.");
}
// TODO: use contact.user_id in actual guard to prevent contact share bypass
await cache.set(ctx.msg.from.id, contact.phone_number.slice(1));
return ctx.reply(
"Phone number successfully linked. /start the bot again to access your Sarafu account."
);
});
bot.start();

6
src/cache.js Normal file
View File

@ -0,0 +1,6 @@
const Redis = require("ioredis");
// TODO: get value from confini
const cache = new Redis();
module.exports = cache;

30
src/request.js Normal file
View File

@ -0,0 +1,30 @@
// npm imports
const phin = require("phin");
// module imports
const cache = require("./cache");
const util = require("./utils");
// proxy requests to ussd-server
// TODO: handle errors
async function proxy(phone, input = "") {
const sessionId = await cache.get(phone);
const { body } = await phin({
// TODO: get value from confini
url: "",
method: "POST",
parse: "string",
form: {
sessionId: sessionId,
phoneNumber: phone,
// TODO: get value from confini
serviceCode: "",
text: input,
},
});
return util.parseUssdResponse(body);
}
module.exports = { proxy };

20
src/utils.js Normal file
View File

@ -0,0 +1,20 @@
// this regex extracts ussd reply options
const regex = /(\d).\s/g;
// TODO: converts the text to a telegram keyboard
function createKeyboard(input) {
return Array.from(input.matchAll(regex), (m) => m[1]);
}
// parses ussd responses to code and text
function parseUssdResponse(input) {
return {
code: input.slice(0, 3),
text: input.slice(4),
};
}
module.exports = {
createKeyboard,
parseUssdResponse,
};

148
yarn.lock Normal file
View File

@ -0,0 +1,148 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@grammyjs/types@^2.5.1":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@grammyjs/types/-/types-2.5.1.tgz#3fab98b6180440bbe5ddfbe4f2097d99c8643444"
integrity sha512-ZJ+X+w5qo6YxCRDWt63+SD2iX+xitP3Z7ieLUWw1b7OanljAMjPRry2PvBRoA1Zg0nFqulxFtiAHxxN1v8BXiQ==
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"
centra@^2.4.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/centra/-/centra-2.5.0.tgz#854c30f9a3ff50da49fa69a8cb441aa25aa1e8e8"
integrity sha512-CnSF1HD8vOOgNbE4P2fZEhdhfAohvpcF3DSdSvEcSHDAZvr+Xfw73isT8SXJJc3VMBqSwjXhr29/ikHUgFcypg==
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==
debug@^4.3.1, debug@^4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
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==
event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
grammy@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/grammy/-/grammy-1.6.1.tgz#7d6390993e830c7330022eddf5f5003054724231"
integrity sha512-ahg+oZCUO6KAYcMXpAEiseR7+JIFyedFLzkdh6Pf7sVjTtLLVG7vQ27ohPTrnTM1tW/J/unlcMdIR8aUgPs/FA==
dependencies:
"@grammyjs/types" "^2.5.1"
abort-controller "^3.0.0"
debug "^4.3.3"
node-fetch "^2.6.5"
ioredis@^4.28.3:
version "4.28.3"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.3.tgz#b13fce8a6a7c525ba22e666d72980a3c0ba799aa"
integrity sha512-9JOWVgBnuSxpIgfpjc1OeY1OLmA4t2KOWWURTDRXky+eWO0LZhI33pQNT9gYxANUXfh5p/zYephYni6GPRsksQ==
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"
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=
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@^2.6.5:
version "2.6.6"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
dependencies:
whatwg-url "^5.0.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==
phin@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/phin/-/phin-3.6.1.tgz#652f61b3dbb0957bc0413a15ef55dfa4e39c303c"
integrity sha512-ubcFKk2jRr2WUJEh+QpMkCgOQansJDuIShKbsjpejeyI/aOMLrAf0TaisbMjKwTq9CsypFFJqUaN9ZSRmBJLPw==
dependencies:
centra "^2.4.2"
redis-commands@1.7.0:
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"
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==
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"