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 { adapter, capabilities, device, info, queryError, querySuccess } from "../types/adapter";
|
||||
import { Address4, Address6 } from 'ip-address';
|
||||
|
||||
interface deviceRaw {
|
||||
|
|
65
src/app.ts
65
src/app.ts
|
@ -1,10 +1,63 @@
|
|||
import { Context, Telegraf } from 'telegraf';
|
||||
import { Update } from 'typegram';
|
||||
import handleInfo from './handler/info';
|
||||
import config from "./config";
|
||||
import { Context, Telegraf, session, Scenes } from "telegraf";
|
||||
import { Update } from "typegram";
|
||||
|
||||
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.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 { adapter } from "../types/adapter";
|
||||
|
||||
export default function handleInfo(adapter: adapter) {
|
||||
return async (ctx: Context)=>{
|
||||
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 {
|
||||
"BGP Route" = "bgp_route",
|
||||
"Ping" = "ping",
|
||||
|
@ -19,7 +17,7 @@ interface device {
|
|||
}
|
||||
|
||||
interface queryResponse {
|
||||
level: "success" | "warning" | "error" | "danger";
|
||||
level: "warning" | "error" | "danger";
|
||||
}
|
||||
|
||||
interface queryError extends queryResponse {
|
||||
|
@ -39,6 +37,6 @@ interface adapter {
|
|||
capability: capabilities,
|
||||
device: device,
|
||||
vrf: string,
|
||||
target: Address4 | Address6
|
||||
): Promise<queryError | querySuccess | queryResponse>;
|
||||
target: import("ip-address").Address4 | import("ip-address").Address6,
|
||||
): Promise<queryError | querySuccess>;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue