mikrotik-telegram 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/actions.d.ts +38 -0
- package/dist/actions.js +40 -0
- package/dist/alerts.d.ts +50 -0
- package/dist/alerts.js +103 -0
- package/dist/audit.d.ts +20 -0
- package/dist/audit.js +44 -0
- package/dist/automation.d.ts +57 -0
- package/dist/automation.js +70 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.js +64 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +34 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +13 -0
- package/dist/mikrotik-service.d.ts +27 -0
- package/dist/mikrotik-service.js +225 -0
- package/dist/rest-client.d.ts +24 -0
- package/dist/rest-client.js +55 -0
- package/dist/runtime-adapters.d.ts +56 -0
- package/dist/runtime-adapters.js +87 -0
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.js +46 -0
- package/dist/telegram-client.d.ts +19 -0
- package/dist/telegram-client.js +29 -0
- package/dist/telegram-commands.d.ts +26 -0
- package/dist/telegram-commands.js +76 -0
- package/dist/types.d.ts +209 -0
- package/dist/types.js +1 -0
- package/package.json +61 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AuthorizationError } from './errors';
|
|
2
|
+
import { RouterOsRestClient } from './rest-client';
|
|
3
|
+
import { MikroTikService } from './mikrotik-service';
|
|
4
|
+
import { TelegramClient } from './telegram-client';
|
|
5
|
+
import { TelegramCommandRegistry } from './telegram-commands';
|
|
6
|
+
function roleFor(config, chatId) {
|
|
7
|
+
if (config.security.adminChatIds.includes(chatId))
|
|
8
|
+
return 'admin';
|
|
9
|
+
if (config.security.operatorChatIds.includes(chatId))
|
|
10
|
+
return 'operator';
|
|
11
|
+
return config.security.defaultRole;
|
|
12
|
+
}
|
|
13
|
+
function contextFromUpdate(config, update) {
|
|
14
|
+
const message = update.message;
|
|
15
|
+
if (!message?.text)
|
|
16
|
+
return undefined;
|
|
17
|
+
const chatId = String(message.chat.id);
|
|
18
|
+
return { context: { chatId, userId: message.from ? String(message.from.id) : undefined, username: message.from?.username, role: roleFor(config, chatId), args: {}, positional: [], rawText: message.text }, text: message.text };
|
|
19
|
+
}
|
|
20
|
+
export function createRuntime(config, fetcher = fetch) {
|
|
21
|
+
const restClient = new RouterOsRestClient({ ...config.mikrotik, fetch: fetcher });
|
|
22
|
+
const mikrotik = new MikroTikService(restClient);
|
|
23
|
+
const telegram = new TelegramClient({ token: config.telegram.token, fetch: fetcher });
|
|
24
|
+
const registry = new TelegramCommandRegistry({ confirmationTtlMs: config.security.confirmationTtlSeconds * 1000 });
|
|
25
|
+
registry.registerMany([
|
|
26
|
+
{ name: 'start', aliases: ['help'], description: 'Afficher les commandes', minimumRole: 'viewer', execute: async () => ({ text: 'NetPulse MikroTik Bot 2026\n/status · /users · /profiles · /sessions · /generate' }) },
|
|
27
|
+
{ name: 'status', aliases: ['health'], description: 'État matériel du routeur', minimumRole: 'viewer', execute: async () => { const health = await mikrotik.getSystemHealth(); return { text: `🟢 ${health.identity} · ${health.model}\nCPU: ${health.cpuLoadPercent}% · RAM libre: ${Math.round(health.freeMemoryBytes / 1048576)} MB\nLatence REST: ${health.latencyMs} ms` }; } },
|
|
28
|
+
{ name: 'users', description: 'Lister les tickets hotspot', minimumRole: 'viewer', execute: async (context) => { const users = await mikrotik.listHotspotUsers({ profile: context.args.profile, limit: Number(context.args.limit || 20) }); return { text: users.length ? users.map((user) => `• ${user.name} (${user.profile || 'default'})`).join('\n') : 'Aucun ticket trouvé.' }; } },
|
|
29
|
+
{ name: 'profiles', description: 'Lister les profils hotspot', minimumRole: 'viewer', execute: async () => { const profiles = await mikrotik.listProfiles(); return { text: profiles.length ? profiles.map((profile) => `• ${profile.name} · ${profile.rateLimit || 'illimité'}`).join('\n') : 'Aucun profil trouvé.' }; } },
|
|
30
|
+
{ name: 'sessions', description: 'Lister les sessions actives', minimumRole: 'viewer', execute: async () => { const sessions = await mikrotik.listActiveSessions(); return { text: sessions.length ? sessions.map((session) => `• ${session.user} · ${session.address || 'IP inconnue'}`).join('\n') : 'Aucune session active.' }; } },
|
|
31
|
+
{ name: 'generate', description: 'Générer des vouchers', minimumRole: 'operator', execute: async (context) => { const result = await mikrotik.generateVouchers({ count: Number(context.args.count || context.positional[0] || 1), profile: context.args.profile, limitUptime: context.args.duration, comment: context.args.comment }); return { text: `✅ ${result.created} voucher(s) créé(s)\n${result.vouchers.map((voucher) => `${voucher.name} · ${voucher.password}`).join('\n')}` }; } },
|
|
32
|
+
]);
|
|
33
|
+
return {
|
|
34
|
+
registry, mikrotik,
|
|
35
|
+
async handleUpdate(update) {
|
|
36
|
+
const incoming = contextFromUpdate(config, update);
|
|
37
|
+
if (!incoming)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (config.telegram.allowedChatIds.length && !config.telegram.allowedChatIds.includes(incoming.context.chatId))
|
|
40
|
+
throw new AuthorizationError('Chat Telegram non autorisé.');
|
|
41
|
+
const response = await registry.execute(incoming.text, incoming.context);
|
|
42
|
+
await telegram.sendMessage(incoming.context.chatId, response);
|
|
43
|
+
return response;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CommandResponse } from './types';
|
|
2
|
+
export interface TelegramClientOptions {
|
|
3
|
+
readonly token: string;
|
|
4
|
+
readonly fetch?: typeof fetch;
|
|
5
|
+
readonly timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface TelegramSendResult {
|
|
8
|
+
readonly messageId: number;
|
|
9
|
+
readonly chatId: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class TelegramClient {
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly fetcher;
|
|
14
|
+
private readonly timeoutMs;
|
|
15
|
+
constructor(options: TelegramClientOptions);
|
|
16
|
+
sendMessage(chatId: string, response: CommandResponse): Promise<TelegramSendResult>;
|
|
17
|
+
answerCallbackQuery(callbackQueryId: string, text?: string): Promise<void>;
|
|
18
|
+
private request;
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class TelegramClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
fetcher;
|
|
4
|
+
timeoutMs;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.baseUrl = `https://api.telegram.org/bot${options.token}`;
|
|
7
|
+
this.fetcher = options.fetch ?? fetch;
|
|
8
|
+
this.timeoutMs = options.timeoutMs ?? 10_000;
|
|
9
|
+
}
|
|
10
|
+
async sendMessage(chatId, response) {
|
|
11
|
+
const result = await this.request('sendMessage', {
|
|
12
|
+
chat_id: chatId, text: response.text, parse_mode: response.parseMode,
|
|
13
|
+
...(response.buttons ? { reply_markup: { inline_keyboard: response.buttons } } : {}),
|
|
14
|
+
});
|
|
15
|
+
return { messageId: result.message_id, chatId };
|
|
16
|
+
}
|
|
17
|
+
async answerCallbackQuery(callbackQueryId, text) {
|
|
18
|
+
await this.request('answerCallbackQuery', { callback_query_id: callbackQueryId, text });
|
|
19
|
+
}
|
|
20
|
+
async request(method, body) {
|
|
21
|
+
const response = await this.fetcher(`${this.baseUrl}/${method}`, {
|
|
22
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: AbortSignal.timeout(this.timeoutMs),
|
|
23
|
+
});
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
if (!response.ok || !data.ok)
|
|
26
|
+
throw new Error(`Telegram API ${method}: ${data.description || response.status}`);
|
|
27
|
+
return data.result;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CommandContext, CommandDefinition, CommandResponse } from './types';
|
|
2
|
+
export interface CommandRegistryOptions {
|
|
3
|
+
readonly confirmationTtlMs?: number;
|
|
4
|
+
readonly now?: () => number;
|
|
5
|
+
readonly idGenerator?: () => string;
|
|
6
|
+
}
|
|
7
|
+
export declare function parseCommand(text: string): {
|
|
8
|
+
name: string;
|
|
9
|
+
args: Readonly<Record<string, string>>;
|
|
10
|
+
positional: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
export declare class TelegramCommandRegistry {
|
|
13
|
+
private readonly commands;
|
|
14
|
+
private readonly pending;
|
|
15
|
+
private readonly confirmationTtlMs;
|
|
16
|
+
private readonly now;
|
|
17
|
+
private readonly idGenerator;
|
|
18
|
+
constructor(options?: CommandRegistryOptions);
|
|
19
|
+
register(definition: CommandDefinition): this;
|
|
20
|
+
registerMany(definitions: readonly CommandDefinition[]): this;
|
|
21
|
+
list(): readonly CommandDefinition[];
|
|
22
|
+
requestConfirmation(context: CommandContext, command: string): CommandResponse;
|
|
23
|
+
consumeConfirmation(id: string, chatId: string): string;
|
|
24
|
+
executeCallback(callbackData: string, chatId: string): Promise<CommandResponse>;
|
|
25
|
+
execute(text: string, context: Omit<CommandContext, 'args' | 'positional' | 'rawText'>): Promise<CommandResponse>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { AuthorizationError, ConfirmationRequiredError } from './errors';
|
|
2
|
+
const ROLE_WEIGHT = { viewer: 1, operator: 2, admin: 3 };
|
|
3
|
+
export function parseCommand(text) {
|
|
4
|
+
const tokens = text.trim().split(/\s+/).filter(Boolean);
|
|
5
|
+
const name = (tokens.shift() || '').replace(/^\//, '').split('@')[0].toLowerCase();
|
|
6
|
+
const args = {};
|
|
7
|
+
const positional = [];
|
|
8
|
+
for (const token of tokens) {
|
|
9
|
+
const separator = token.indexOf('=');
|
|
10
|
+
if (separator > 0)
|
|
11
|
+
args[token.slice(0, separator).toLowerCase()] = token.slice(separator + 1);
|
|
12
|
+
else
|
|
13
|
+
positional.push(token);
|
|
14
|
+
}
|
|
15
|
+
return { name, args, positional };
|
|
16
|
+
}
|
|
17
|
+
export class TelegramCommandRegistry {
|
|
18
|
+
commands = new Map();
|
|
19
|
+
pending = new Map();
|
|
20
|
+
confirmationTtlMs;
|
|
21
|
+
now;
|
|
22
|
+
idGenerator;
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.confirmationTtlMs = options.confirmationTtlMs ?? 60_000;
|
|
25
|
+
this.now = options.now ?? Date.now;
|
|
26
|
+
this.idGenerator = options.idGenerator ?? (() => Math.random().toString(36).slice(2, 12));
|
|
27
|
+
}
|
|
28
|
+
register(definition) {
|
|
29
|
+
const names = [definition.name, ...(definition.aliases || [])];
|
|
30
|
+
for (const name of names)
|
|
31
|
+
this.commands.set(name.toLowerCase().replace(/^\//, ''), definition);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
registerMany(definitions) {
|
|
35
|
+
definitions.forEach((definition) => this.register(definition));
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
list() {
|
|
39
|
+
return [...new Set(this.commands.values())];
|
|
40
|
+
}
|
|
41
|
+
requestConfirmation(context, command) {
|
|
42
|
+
const id = this.idGenerator();
|
|
43
|
+
this.pending.set(id, { id, chatId: context.chatId, command, expiresAt: this.now() + this.confirmationTtlMs });
|
|
44
|
+
return { text: `Confirmation requise pour ${command}.`, buttons: [[{ text: 'Confirmer', callbackData: `confirm:${id}` }, { text: 'Annuler', callbackData: `cancel:${id}` }]] };
|
|
45
|
+
}
|
|
46
|
+
consumeConfirmation(id, chatId) {
|
|
47
|
+
const pending = this.pending.get(id);
|
|
48
|
+
this.pending.delete(id);
|
|
49
|
+
if (!pending || pending.chatId !== chatId || pending.expiresAt < this.now())
|
|
50
|
+
throw new ConfirmationRequiredError(id, 'Confirmation absente ou expirée.');
|
|
51
|
+
return pending.command;
|
|
52
|
+
}
|
|
53
|
+
async executeCallback(callbackData, chatId) {
|
|
54
|
+
if (callbackData.startsWith('confirm:')) {
|
|
55
|
+
const id = callbackData.slice('confirm:'.length);
|
|
56
|
+
const command = this.consumeConfirmation(id, chatId);
|
|
57
|
+
return { text: `Action confirmée: ${command}` };
|
|
58
|
+
}
|
|
59
|
+
if (callbackData.startsWith('cancel:')) {
|
|
60
|
+
const id = callbackData.slice('cancel:'.length);
|
|
61
|
+
this.pending.delete(id);
|
|
62
|
+
return { text: 'Action annulée. Aucune modification n’a été effectuée.' };
|
|
63
|
+
}
|
|
64
|
+
return { text: 'Callback inconnue.' };
|
|
65
|
+
}
|
|
66
|
+
async execute(text, context) {
|
|
67
|
+
const parsed = parseCommand(text);
|
|
68
|
+
const definition = this.commands.get(parsed.name);
|
|
69
|
+
if (!definition)
|
|
70
|
+
return { text: 'Commande inconnue. Utilisez /help.' };
|
|
71
|
+
const fullContext = { ...context, ...parsed, rawText: text };
|
|
72
|
+
if (ROLE_WEIGHT[fullContext.role] < ROLE_WEIGHT[definition.minimumRole])
|
|
73
|
+
throw new AuthorizationError();
|
|
74
|
+
return definition.execute(fullContext);
|
|
75
|
+
}
|
|
76
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
export type RouterId = string;
|
|
2
|
+
export interface RouterRecord {
|
|
3
|
+
readonly '.id'?: string;
|
|
4
|
+
readonly [key: string]: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
export interface RouterPayload {
|
|
7
|
+
readonly [key: string]: string | number | boolean | null | undefined;
|
|
8
|
+
}
|
|
9
|
+
export type RouterResponse = RouterRecord | RouterRecord[] | string | null;
|
|
10
|
+
export interface SystemHealth {
|
|
11
|
+
readonly identity: string;
|
|
12
|
+
readonly model: string;
|
|
13
|
+
readonly version: string;
|
|
14
|
+
readonly uptime: string;
|
|
15
|
+
readonly cpuLoadPercent: number;
|
|
16
|
+
readonly totalMemoryBytes: number;
|
|
17
|
+
readonly freeMemoryBytes: number;
|
|
18
|
+
readonly totalStorageBytes: number;
|
|
19
|
+
readonly freeStorageBytes: number;
|
|
20
|
+
readonly latencyMs: number;
|
|
21
|
+
}
|
|
22
|
+
export interface HotspotUser {
|
|
23
|
+
readonly id: RouterId;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly password?: string;
|
|
26
|
+
readonly profile?: string;
|
|
27
|
+
readonly comment?: string;
|
|
28
|
+
readonly limitUptime?: string;
|
|
29
|
+
readonly uptime?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface HotspotProfile {
|
|
32
|
+
readonly id: RouterId;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly rateLimit?: string;
|
|
35
|
+
readonly sharedUsers?: number;
|
|
36
|
+
readonly onLogin?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ActiveSession {
|
|
39
|
+
readonly id: RouterId;
|
|
40
|
+
readonly user: string;
|
|
41
|
+
readonly address?: string;
|
|
42
|
+
readonly macAddress?: string;
|
|
43
|
+
readonly uptime?: string;
|
|
44
|
+
readonly sessionTimeLeft?: string;
|
|
45
|
+
readonly bytesIn: number;
|
|
46
|
+
readonly bytesOut: number;
|
|
47
|
+
}
|
|
48
|
+
export interface RouterScript {
|
|
49
|
+
readonly id: RouterId;
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly source: string;
|
|
52
|
+
readonly comment?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface RouterScheduler {
|
|
55
|
+
readonly id: RouterId;
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly interval: string;
|
|
58
|
+
readonly onEvent: string;
|
|
59
|
+
readonly disabled: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface MutationResult {
|
|
62
|
+
readonly success: boolean;
|
|
63
|
+
readonly id?: RouterId;
|
|
64
|
+
readonly operation: string;
|
|
65
|
+
}
|
|
66
|
+
export interface BanUserInput {
|
|
67
|
+
readonly address?: string;
|
|
68
|
+
readonly user?: string;
|
|
69
|
+
readonly comment?: string;
|
|
70
|
+
readonly timeout?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface BanResult extends MutationResult {
|
|
73
|
+
readonly address?: string;
|
|
74
|
+
readonly ruleId?: RouterId;
|
|
75
|
+
}
|
|
76
|
+
export interface UnbanUserInput {
|
|
77
|
+
readonly ruleId?: RouterId;
|
|
78
|
+
readonly address?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface DeleteUserInput {
|
|
81
|
+
readonly id: RouterId;
|
|
82
|
+
readonly reason?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface DisconnectUserInput {
|
|
85
|
+
readonly sessionId: RouterId;
|
|
86
|
+
readonly reason?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface UserQuery {
|
|
89
|
+
readonly name?: string;
|
|
90
|
+
readonly profile?: string;
|
|
91
|
+
readonly limit?: number;
|
|
92
|
+
}
|
|
93
|
+
export interface CreateHotspotUserInput {
|
|
94
|
+
readonly name: string;
|
|
95
|
+
readonly password: string;
|
|
96
|
+
readonly profile?: string;
|
|
97
|
+
readonly comment?: string;
|
|
98
|
+
readonly limitUptime?: string;
|
|
99
|
+
}
|
|
100
|
+
export interface GenerateVouchersInput {
|
|
101
|
+
readonly count: number;
|
|
102
|
+
readonly length?: number;
|
|
103
|
+
readonly prefix?: string;
|
|
104
|
+
readonly profile?: string;
|
|
105
|
+
readonly passwordLength?: number;
|
|
106
|
+
readonly limitUptime?: string;
|
|
107
|
+
readonly comment?: string;
|
|
108
|
+
readonly price?: number;
|
|
109
|
+
readonly expiresAt?: string;
|
|
110
|
+
readonly dryRun?: boolean;
|
|
111
|
+
readonly duplicateCheck?: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface TrafficMetrics {
|
|
114
|
+
readonly online: boolean;
|
|
115
|
+
readonly rxBytesPerSecond?: number;
|
|
116
|
+
readonly txBytesPerSecond?: number;
|
|
117
|
+
readonly totalSessions?: number;
|
|
118
|
+
readonly peakSessions?: number;
|
|
119
|
+
readonly topUser?: string;
|
|
120
|
+
}
|
|
121
|
+
export interface GeneratedVoucher {
|
|
122
|
+
readonly name: string;
|
|
123
|
+
readonly password: string;
|
|
124
|
+
readonly profile: string;
|
|
125
|
+
readonly created: boolean;
|
|
126
|
+
readonly price?: number;
|
|
127
|
+
readonly expiresAt?: string;
|
|
128
|
+
}
|
|
129
|
+
export interface GenerateVouchersResult {
|
|
130
|
+
readonly requested: number;
|
|
131
|
+
readonly created: number;
|
|
132
|
+
readonly vouchers: readonly GeneratedVoucher[];
|
|
133
|
+
readonly dryRun: boolean;
|
|
134
|
+
readonly duplicateSkipped?: number;
|
|
135
|
+
}
|
|
136
|
+
export type UpdateHotspotUserInput = Partial<CreateHotspotUserInput>;
|
|
137
|
+
export interface CreateProfileInput {
|
|
138
|
+
readonly name: string;
|
|
139
|
+
readonly rateLimit?: string;
|
|
140
|
+
readonly sharedUsers?: number;
|
|
141
|
+
readonly onLogin?: string;
|
|
142
|
+
}
|
|
143
|
+
export type UpdateProfileInput = Partial<CreateProfileInput>;
|
|
144
|
+
export interface CreateScriptInput {
|
|
145
|
+
readonly name: string;
|
|
146
|
+
readonly source: string;
|
|
147
|
+
readonly comment?: string;
|
|
148
|
+
}
|
|
149
|
+
export interface CreateSchedulerInput {
|
|
150
|
+
readonly name: string;
|
|
151
|
+
readonly interval: string;
|
|
152
|
+
readonly onEvent: string;
|
|
153
|
+
readonly comment?: string;
|
|
154
|
+
readonly disabled?: boolean;
|
|
155
|
+
}
|
|
156
|
+
export type UpdateSchedulerInput = Partial<CreateSchedulerInput>;
|
|
157
|
+
export interface TelegramUpdate {
|
|
158
|
+
readonly update_id: number;
|
|
159
|
+
readonly message?: {
|
|
160
|
+
readonly message_id: number;
|
|
161
|
+
readonly text?: string;
|
|
162
|
+
readonly from?: {
|
|
163
|
+
readonly id: number;
|
|
164
|
+
readonly username?: string;
|
|
165
|
+
};
|
|
166
|
+
readonly chat: {
|
|
167
|
+
readonly id: number | string;
|
|
168
|
+
readonly type: string;
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
readonly callback_query?: {
|
|
172
|
+
readonly id: string;
|
|
173
|
+
readonly data?: string;
|
|
174
|
+
readonly from: {
|
|
175
|
+
readonly id: number;
|
|
176
|
+
readonly username?: string;
|
|
177
|
+
};
|
|
178
|
+
readonly message?: {
|
|
179
|
+
readonly chat: {
|
|
180
|
+
readonly id: number | string;
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
export type TelegramRole = 'admin' | 'operator' | 'viewer';
|
|
186
|
+
export interface CommandContext {
|
|
187
|
+
readonly chatId: string;
|
|
188
|
+
readonly userId?: string;
|
|
189
|
+
readonly username?: string;
|
|
190
|
+
readonly role: TelegramRole;
|
|
191
|
+
readonly args: Readonly<Record<string, string>>;
|
|
192
|
+
readonly positional: readonly string[];
|
|
193
|
+
readonly rawText: string;
|
|
194
|
+
}
|
|
195
|
+
export interface CommandResponse {
|
|
196
|
+
readonly text: string;
|
|
197
|
+
readonly parseMode?: 'MarkdownV2' | 'HTML';
|
|
198
|
+
readonly buttons?: readonly (readonly {
|
|
199
|
+
readonly text: string;
|
|
200
|
+
readonly callbackData: string;
|
|
201
|
+
}[])[];
|
|
202
|
+
}
|
|
203
|
+
export interface CommandDefinition {
|
|
204
|
+
readonly name: string;
|
|
205
|
+
readonly aliases?: readonly string[];
|
|
206
|
+
readonly description: string;
|
|
207
|
+
readonly minimumRole: TelegramRole;
|
|
208
|
+
readonly execute: (context: CommandContext) => Promise<CommandResponse>;
|
|
209
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mikrotik-telegram",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Type-safe RouterOS REST automation and Telegram bot package for modern MikroTik operations.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"CHANGELOG.md"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/netpulse/netpulse-mikrotik-telegram.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/netpulse/netpulse-mikrotik-telegram",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/netpulse/netpulse-mikrotik-telegram/issues"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"mikrotik",
|
|
41
|
+
"routeros",
|
|
42
|
+
"rest",
|
|
43
|
+
"telegram",
|
|
44
|
+
"hotspot",
|
|
45
|
+
"automation",
|
|
46
|
+
"bun",
|
|
47
|
+
"node",
|
|
48
|
+
"serverless"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc -p tsconfig.build.json",
|
|
52
|
+
"prepack": "bun run build",
|
|
53
|
+
"test": "bun test src/index.test.ts"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"zod": "^4.6.1"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"typescript": "^5.9.3"
|
|
60
|
+
}
|
|
61
|
+
}
|