mirror of https://github.com/186526/net186-bot
First usable version
This commit is contained in:
parent
2a5e1b74c9
commit
799e1ace7b
|
@ -1,5 +1,4 @@
|
||||||
import axios, { AxiosInstance } from "axios";
|
import axios, { AxiosInstance } from "axios";
|
||||||
import { adapter, capabilities, device, info, queryError, querySuccess } from "../types/adapter";
|
|
||||||
import { Address4, Address6 } from 'ip-address';
|
import { Address4, Address6 } from 'ip-address';
|
||||||
|
|
||||||
interface deviceRaw {
|
interface deviceRaw {
|
||||||
|
|
65
src/app.ts
65
src/app.ts
|
@ -1,10 +1,63 @@
|
||||||
import { Context, Telegraf } from 'telegraf';
|
import { Context, Telegraf, session, Scenes } from "telegraf";
|
||||||
import { Update } from 'typegram';
|
import { Update } from "typegram";
|
||||||
import handleInfo from './handler/info';
|
|
||||||
import config from "./config";
|
|
||||||
|
|
||||||
const bot: Telegraf<Context<Update>> = new Telegraf(config.token);
|
import handleList from "./handler/list";
|
||||||
|
import queryInit, { queryContext } from "./handler/query";
|
||||||
|
import handleInfo from "./handler/info";
|
||||||
|
|
||||||
|
import config from "./config";
|
||||||
|
import "./polyfill";
|
||||||
|
|
||||||
|
const bot: Telegraf<Context<Update> & queryContext> = new Telegraf(
|
||||||
|
config.token
|
||||||
|
);
|
||||||
|
|
||||||
bot.start(handleInfo(config.adapter));
|
bot.start(handleInfo(config.adapter));
|
||||||
|
|
||||||
bot.launch();
|
bot.help((ctx) => {
|
||||||
|
ctx.reply(
|
||||||
|
`/info - show information.
|
||||||
|
/list - show devices.
|
||||||
|
/route - show BGP route.
|
||||||
|
/ping - ping command.
|
||||||
|
/traceroute - traceroute command.
|
||||||
|
`,
|
||||||
|
{ reply_to_message_id: ctx.message?.message_id }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.use(session());
|
||||||
|
|
||||||
|
const stage = new Scenes.Stage<queryContext>([
|
||||||
|
queryInit("traceroute" as capabilities, config.adapter),
|
||||||
|
queryInit("ping" as capabilities, config.adapter),
|
||||||
|
queryInit("bgp_route" as capabilities, config.adapter),
|
||||||
|
]);
|
||||||
|
|
||||||
|
bot.use(stage.middleware());
|
||||||
|
|
||||||
|
bot.use((ctx, next) => {
|
||||||
|
console.log(
|
||||||
|
`INFO: Message from ${ctx.message?.from.id}@${ctx.message?.chat.id} in ${ctx.message?.date}`
|
||||||
|
);
|
||||||
|
if ((ctx.message?.date ?? 0) < ((new Date().getTime() / 1000) | 0) - 120) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.command("info", handleInfo(config.adapter));
|
||||||
|
bot.command("list", handleList(config.adapter));
|
||||||
|
bot.command("route", (ctx) => ctx.scene.enter("bgp_route"));
|
||||||
|
bot.command("ping", (ctx) => ctx.scene.enter("ping"));
|
||||||
|
bot.command("traceroute", (ctx) => ctx.scene.enter("traceroute"));
|
||||||
|
|
||||||
|
process.once("SIGINT", () => bot.stop("SIGINT"));
|
||||||
|
process.once("SIGTERM", () => bot.stop("SIGTERM"));
|
||||||
|
|
||||||
|
bot.launch();
|
||||||
|
|
||||||
|
bot.catch((err, ctx) => {
|
||||||
|
console.log(ctx, err);
|
||||||
|
ctx.editMessageText(`${err}`);
|
||||||
|
});
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Context } from "telegraf";
|
import { Context } from "telegraf";
|
||||||
import { adapter } from "../types/adapter";
|
|
||||||
|
|
||||||
export default function handleInfo(adapter: adapter) {
|
export default function handleInfo(adapter: adapter) {
|
||||||
return async (ctx: Context)=>{
|
return async (ctx: Context)=>{
|
||||||
const info = await adapter.info();
|
const info = await adapter.info();
|
||||||
ctx.reply(`${info.name} Bot from ${info.organization}.`)
|
await ctx.reply(`${info.name} Bot from ${info.organization}.`, {
|
||||||
|
reply_to_message_id: ctx.message?.message_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Context } from "telegraf";
|
||||||
|
|
||||||
|
export default function handleList(adapter: adapter) {
|
||||||
|
return async (ctx: Context) => {
|
||||||
|
const devices = await adapter.devices();
|
||||||
|
let map: { [key: string]: device[] } = {};
|
||||||
|
devices.forEach(
|
||||||
|
(device) =>
|
||||||
|
(map[device.group] =
|
||||||
|
typeof map[device.group] == "object"
|
||||||
|
? [...map[device.group], device]
|
||||||
|
: [device])
|
||||||
|
);
|
||||||
|
await ctx.replyWithHTML(Object.keys(map).map(key => `<b>${key}</b>: \n${map[key].map(dev=>`${dev.name}`).join("\n")}`).join("\n\n"),{
|
||||||
|
reply_to_message_id: ctx.message?.message_id
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { Context, deunionize, Scenes, session } from "telegraf";
|
||||||
|
import Address from "../lib/address";
|
||||||
|
import type { Address4, Address6 } from "ip-address";
|
||||||
|
|
||||||
|
interface querySession extends Scenes.SceneSession {
|
||||||
|
choosenDevice: device;
|
||||||
|
devices: device[];
|
||||||
|
address: Address4 | Address6;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface queryContext extends Context {
|
||||||
|
session: querySession;
|
||||||
|
|
||||||
|
scene: Scenes.SceneContextScene<queryContext>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (capability: capabilities, adapter: adapter) => {
|
||||||
|
const queryScene = new Scenes.BaseScene<queryContext>(capability);
|
||||||
|
queryScene.enter(async (ctx) => {
|
||||||
|
let text = deunionize(ctx.message)?.text;
|
||||||
|
if (!text) return;
|
||||||
|
let command = text.split(" ").filter((k) => k);
|
||||||
|
|
||||||
|
if (command.length == 1) {
|
||||||
|
ctx.reply(`Usage: ${text} target [Device]`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originMessage = await ctx.reply("Querying Looking Glass...", {
|
||||||
|
reply_to_message_id: ctx.message?.message_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.session.address = Address(command[1]);
|
||||||
|
} catch (e: any) {
|
||||||
|
await ctx.telegram.editMessageText(
|
||||||
|
originMessage.chat.id,
|
||||||
|
originMessage.message_id,
|
||||||
|
undefined,
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.session.devices = await adapter.devices();
|
||||||
|
|
||||||
|
if (command.length == 2) {
|
||||||
|
await ctx.telegram.editMessageText(
|
||||||
|
originMessage.chat.id,
|
||||||
|
originMessage.message_id,
|
||||||
|
undefined,
|
||||||
|
"Please select the device you want to query about.",
|
||||||
|
{
|
||||||
|
reply_markup: {
|
||||||
|
inline_keyboard: ctx.session.devices.reduce(
|
||||||
|
(
|
||||||
|
pre: {
|
||||||
|
text: string;
|
||||||
|
callback_data: string;
|
||||||
|
}[][],
|
||||||
|
cur
|
||||||
|
) => {
|
||||||
|
if (pre[pre.length - 1].length == 2) pre.push([]);
|
||||||
|
pre[pre.length - 1].push({
|
||||||
|
text: cur.name,
|
||||||
|
callback_data: cur.name,
|
||||||
|
});
|
||||||
|
return pre;
|
||||||
|
},
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: "Random Device",
|
||||||
|
callback_data: "random",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else if (command.length == 3) {
|
||||||
|
ctx.session.choosenDevice =
|
||||||
|
ctx.session.devices.find((v) =>
|
||||||
|
v.name.toLocaleLowerCase().includes(command[2].toLocaleLowerCase())
|
||||||
|
) ?? ctx.session.devices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await adapter.query(
|
||||||
|
capability,
|
||||||
|
ctx.session.choosenDevice,
|
||||||
|
ctx.session.choosenDevice.vrfs[0],
|
||||||
|
ctx.session.address
|
||||||
|
);
|
||||||
|
|
||||||
|
await ctx.telegram.editMessageText(
|
||||||
|
originMessage.chat.id,
|
||||||
|
originMessage.message_id,
|
||||||
|
undefined,
|
||||||
|
`Query Result from <code>${ctx.session.choosenDevice.name}</code>.\n<code>${result.output}</code>`,
|
||||||
|
{ parse_mode: "HTML" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
queryScene.action(/.+/, async (ctx) => {
|
||||||
|
if (ctx.match[0] != "random") {
|
||||||
|
ctx.session.choosenDevice =
|
||||||
|
ctx.session.devices.find((v) => v.name.includes(ctx.match[0])) ??
|
||||||
|
ctx.session.devices[0];
|
||||||
|
} else
|
||||||
|
ctx.session.choosenDevice =
|
||||||
|
ctx.session.devices[(Math.random() * ctx.session.devices.length) | 0];
|
||||||
|
|
||||||
|
const result = await adapter.query(
|
||||||
|
capability,
|
||||||
|
ctx.session.choosenDevice,
|
||||||
|
ctx.session.choosenDevice.vrfs[0],
|
||||||
|
ctx.session.address
|
||||||
|
);
|
||||||
|
|
||||||
|
await ctx.editMessageText(
|
||||||
|
`Query Result from <code>${ctx.session.choosenDevice.name}</code>.\n<code>${result.output}</code>`,
|
||||||
|
{ parse_mode: "HTML" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return queryScene;
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Address4, Address6 } from "ip-address";
|
||||||
|
import { isIPv4, isIPv6 } from "net";
|
||||||
|
|
||||||
|
export default (address: string): Address4 | Address6 => {
|
||||||
|
if (isIPv4(address)) return new Address4(address);
|
||||||
|
if (isIPv6(address)) return new Address6(address);
|
||||||
|
throw new Error("Invalid address");
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
if (!String.prototype.replaceAll) {
|
||||||
|
String.prototype.replaceAll = function (
|
||||||
|
searchValue: string | RegExp,
|
||||||
|
replaceValue: string | ((substring: string, ...args: any[]) => string)
|
||||||
|
): string {
|
||||||
|
if (typeof replaceValue == "function") throw new Error("Not Supported.");
|
||||||
|
// If a regex pattern
|
||||||
|
if (
|
||||||
|
Object.prototype.toString.call(searchValue).toLowerCase() ===
|
||||||
|
"[object regexp]"
|
||||||
|
) {
|
||||||
|
return this.replace(searchValue, replaceValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a string
|
||||||
|
return this.replace(new RegExp(searchValue, "g"), replaceValue);
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
import { Address4, Address6 } from "ip-address";
|
|
||||||
|
|
||||||
enum capabilities {
|
enum capabilities {
|
||||||
"BGP Route" = "bgp_route",
|
"BGP Route" = "bgp_route",
|
||||||
"Ping" = "ping",
|
"Ping" = "ping",
|
||||||
|
@ -19,7 +17,7 @@ interface device {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface queryResponse {
|
interface queryResponse {
|
||||||
level: "success" | "warning" | "error" | "danger";
|
level: "warning" | "error" | "danger";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface queryError extends queryResponse {
|
interface queryError extends queryResponse {
|
||||||
|
@ -39,6 +37,6 @@ interface adapter {
|
||||||
capability: capabilities,
|
capability: capabilities,
|
||||||
device: device,
|
device: device,
|
||||||
vrf: string,
|
vrf: string,
|
||||||
target: Address4 | Address6
|
target: import("ip-address").Address4 | import("ip-address").Address6,
|
||||||
): Promise<queryError | querySuccess | queryResponse>;
|
): Promise<queryError | querySuccess>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue