@kotori-bot/kotori-plugin-webui 1.0.0 → 1.1.1

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/README.md CHANGED
@@ -1,7 +1,13 @@
1
- # kotori-bot-admin-server
2
-
3
- Web front end repository: [kotorijs/webui](https://github.com/kotorijs/webui)
4
-
5
- ## Reference
6
-
7
- - [Kotori Docs](https://kotori.js.org/)
1
+ # kotori-bot-admin-server
2
+
3
+ Web front end repository: [kotorijs/webui](https://github.com/kotorijs/webui)
4
+
5
+ ## Reference
6
+
7
+ - [Kotori Docs](https://kotori.js.org/)
8
+
9
+ ## TODO
10
+
11
+ - [] Add authorization for websocket server
12
+ - [] Login at the same time
13
+ - [] Console outputting support for cmd adapter
package/lib/index.js CHANGED
@@ -1,128 +1,41 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
4
  };
25
5
  Object.defineProperty(exports, "__esModule", { value: true });
26
6
  exports.main = exports.config = exports.inject = void 0;
27
- const kotori_bot_1 = require("kotori-bot");
28
- const path_1 = __importStar(require("path"));
29
- const fs_1 = require("fs");
30
- const common_1 = require("./common");
31
- const const_1 = require("./const");
32
- exports.inject = ['server', 'file'];
33
- exports.config = kotori_bot_1.Tsu.Object({
34
- username: kotori_bot_1.Tsu.String().default(const_1.DEFAULT_USERNAME),
35
- password: kotori_bot_1.Tsu.String().default(const_1.DEFAULT_PASSWORD)
36
- });
37
- function main(ctx, con) {
38
- const loadToken = () => ctx.file.load('token', 'txt');
39
- const updateToken = () => ctx.file.save('token', (0, common_1.getToken)(), 'txt');
40
- const checkToken = (token) => token && token === loadToken();
7
+ const path_1 = __importDefault(require("path"));
8
+ const webui_1 = require("./utils/webui");
9
+ const routers_1 = __importDefault(require("./routers"));
10
+ const ws_1 = __importDefault(require("./ws"));
11
+ const router_1 = __importDefault(require("./routers/router"));
12
+ exports.inject = ['server', 'file', 'cache'];
13
+ var webui_2 = require("./utils/webui");
14
+ Object.defineProperty(exports, "config", { enumerable: true, get: function () { return webui_2.config; } });
15
+ function main(ctx, cfg) {
16
+ /* Starts webui service */
17
+ ctx.service('webui', new webui_1.Webui(ctx, cfg));
18
+ ctx.inject('webui');
19
+ ctx.on('ready', () => {
20
+ (0, ws_1.default)(ctx, ctx.server.wss('/webui'));
21
+ });
22
+ /* Sets up routes */
41
23
  const app = ctx.server;
42
24
  app.use(app.static(path_1.default.resolve(__dirname, '../dist')));
43
25
  app.use(app.json());
44
26
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
45
27
  app.all('*', (req, res, next) => {
46
- if (req.path !== '/api/info')
47
- ctx.logger.label(req.method).trace(req.path);
28
+ ctx.logger.label(req.method).trace(req.path);
48
29
  res.header('Access-Control-Allow-Origin', '*');
49
30
  res.header('Access-Control-Allow-Headers', 'Content-Type');
50
31
  res.header('Access-Control-Allow-Methods', '*');
51
32
  res.header('Content-Type', 'application/json;charset=utf-8');
52
- if (req.path !== '/api/login' && !checkToken(String(req.query.token))) {
53
- res.status(200).json({});
54
- return;
55
- }
56
- next();
57
- });
58
- const router = app.router();
59
- router.get('/bots', (_, res) => {
60
- const bots = [];
61
- ctx[kotori_bot_1.Symbols.bot].forEach((bot) => bot.forEach((api) => bots.push({
62
- ...api.adapter.status,
63
- platform: api.adapter.platform,
64
- identity: api.adapter.identity,
65
- id: api.adapter.selfId,
66
- lang: api.adapter.config.lang
67
- })));
68
- res.status(200).json(bots);
69
- });
70
- router.get('/modules', (_, res) => {
71
- const modules = [];
72
- ctx[kotori_bot_1.Symbols.modules].forEach((module) => modules.push([module[0].pkg, module[1]]));
73
- res.status(200).json(modules);
74
- });
75
- router.get('/data', (_, res) => {
76
- res.status(200).json({
77
- midwares: ctx[kotori_bot_1.Symbols.midware].size,
78
- commands: ctx[kotori_bot_1.Symbols.command].size,
79
- regexps: ctx[kotori_bot_1.Symbols.regexp].size,
80
- bots: ctx[kotori_bot_1.Symbols.bot].size,
81
- adapters: ctx[kotori_bot_1.Symbols.adapter].size,
82
- modules: ctx[kotori_bot_1.Symbols.modules].size,
83
- dir: ctx.baseDir.root,
84
- pkg: ctx.pkg,
85
- node: process.version
86
- });
87
- });
88
- router.get('/info', (_, res) => {
89
- res.status(200).json({
90
- cpu: (0, common_1.getCpuRate)(),
91
- ram: (0, common_1.getRamRate)(),
92
- version: JSON.parse((0, fs_1.readFileSync)((0, path_1.resolve)(__dirname, '../package.json')).toString()).version
93
- });
94
- });
95
- router.get('/login', (req, res) => {
96
- const { user, pwd } = req.query;
97
- if (user === con.username && pwd === con.password) {
98
- updateToken();
99
- res
100
- .status(200)
101
- .json({ token: loadToken(), default: con.username === const_1.DEFAULT_USERNAME && con.password === const_1.DEFAULT_PASSWORD });
102
- }
103
- else {
104
- res.status(200).json({});
105
- }
106
- });
107
- router.get('/config', (req, res) => {
108
- const { type, id } = req.query;
109
- if (type === 'module') {
110
- if (typeof id === 'string') {
111
- const split = id.split('/');
112
- const handle = (0, kotori_bot_1.stringRightSplit)(split.length > 1 ? split[1] : split[0], kotori_bot_1.PLUGIN_PREFIX);
113
- res.status(200).json(handle in ctx.config.plugin ? ctx.config.plugin[handle] : {});
114
- }
115
- else {
116
- res.status(200).json({});
117
- }
118
- }
119
- else if (type === 'adapter') {
120
- res.status(200).json(typeof id === 'string' && id in ctx.config.adapter ? ctx.config.adapter[id] : {});
121
- }
122
- else {
123
- res.status(200).json(ctx.config);
124
- }
33
+ if (!router_1.default.find((item) => item.path === req.path || req.path.startsWith(item.path)))
34
+ return res.status(404).send();
35
+ if (req.path === '/api/accounts/login' || ctx.webui.checkToken(req.headers.authorization))
36
+ return next();
37
+ return res.status(401);
125
38
  });
126
- app.use('/api', router);
39
+ app.use('/', (0, routers_1.default)(ctx, app));
127
40
  }
128
41
  exports.main = main;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const constant_1 = require("../../constant");
4
+ /* eslint-disable-next-line func-names */
5
+ function default_1(ctx, app) {
6
+ const router = app.router();
7
+ router.post('/login', (req, res) => {
8
+ const { username: user, password: pwd } = req.body;
9
+ const { username, password } = ctx.webui.config;
10
+ const loginStats = ctx.webui.getLoginStats();
11
+ if (user === username && pwd === password) {
12
+ ctx.logger.label('server').trace('Login successful');
13
+ loginStats.success += 1;
14
+ ctx.webui.setLoginStats(loginStats);
15
+ ctx.webui.updateToken();
16
+ return res.json({
17
+ token: ctx.webui.getToken(),
18
+ isDefault: username + password === constant_1.DEFAULT_USERNAME + constant_1.DEFAULT_PASSWORD
19
+ });
20
+ }
21
+ ctx.logger.label('server').trace('Login failed');
22
+ loginStats.failed += 1;
23
+ ctx.webui.setLoginStats(loginStats);
24
+ return res.status(401).json({ message: 'Invalid username or password' });
25
+ });
26
+ router.post('/logout', (_, res) => {
27
+ ctx.webui.updateToken();
28
+ res.status(204).send();
29
+ });
30
+ return router;
31
+ }
32
+ exports.default = default_1;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const kotori_bot_1 = require("kotori-bot");
4
+ /* eslint-disable-next-line func-names */
5
+ function default_1(ctx, app) {
6
+ const getPluginConfig = () => Object.entries(ctx.config.plugin).map(([name, origin]) => ({ name, origin, schema: {} }));
7
+ const getBotConfig = () => Object.entries(ctx.config.adapter).map(([id, origin]) => ({ id, origin, schema: {} }));
8
+ const router = app.router();
9
+ router.get('/plugins/:scope?/:name?', (req, res) => {
10
+ const { scope, name } = req.params;
11
+ if (!scope)
12
+ return res.json(getPluginConfig());
13
+ const pluginName = (0, kotori_bot_1.stringRightSplit)(name ?? scope, kotori_bot_1.PLUGIN_PREFIX);
14
+ const pluginConfig = getPluginConfig().find((plugin) => plugin.name === pluginName);
15
+ return pluginConfig ? res.json(pluginConfig) : res.status(404).json({ message: 'Plugin not found' });
16
+ });
17
+ router.put('/plugins/:scope?/:name?', (req, res) => {
18
+ const { scope, name } = req.params;
19
+ const { body } = req;
20
+ if (!scope || !name)
21
+ return res.status(400).json({ message: 'Invalid plugin scope' });
22
+ if (typeof body !== 'object')
23
+ return res.status(400).json({ message: 'Invalid body' });
24
+ const pluginName = (0, kotori_bot_1.stringRightSplit)(name ?? scope, kotori_bot_1.PLUGIN_PREFIX);
25
+ const pluginConfig = getPluginConfig().find((plugin) => plugin.name === pluginName);
26
+ if (!pluginConfig)
27
+ return res.status(404).json({ message: 'Plugin not found' });
28
+ Object.keys(body).forEach((key) => {
29
+ if (key in pluginConfig)
30
+ pluginConfig[key] = body[key];
31
+ });
32
+ return res.status(204).send();
33
+ });
34
+ router.get('/bots/:name?', (req, res) => {
35
+ const { name } = req.params;
36
+ if (!name)
37
+ return res.json(getBotConfig());
38
+ const botConfig = getBotConfig().find((bot) => bot.id === name);
39
+ return botConfig ? res.json(botConfig) : res.status(404).json({ message: 'Bot not found' });
40
+ });
41
+ router.put('/bots/:name?', (req, res) => {
42
+ const { name } = req.params;
43
+ const { body } = req;
44
+ if (!name)
45
+ return res.status(400).json({ message: 'Invalid bot name' });
46
+ if (typeof body !== 'object')
47
+ return res.status(400).json({ message: 'Invalid body' });
48
+ const botConfig = ctx.config.adapter[name];
49
+ if (!botConfig)
50
+ return res.status(404).json({ message: 'Bot not found' });
51
+ Object.keys(body).forEach((key) => {
52
+ if (key in botConfig)
53
+ botConfig[key] = body[key];
54
+ });
55
+ return res.status(204).send();
56
+ });
57
+ router.get('/global', (_, res) => {
58
+ res.json(ctx.config.global);
59
+ });
60
+ router.put('/global', (req, res) => {
61
+ const { body } = req;
62
+ if (typeof body !== 'object')
63
+ return res.status(400).json({ message: 'Invalid body' });
64
+ Object.keys(body).forEach((key) => {
65
+ if (key in ctx.config.global)
66
+ ctx.config.global[key] = body[key];
67
+ });
68
+ return res.status(204).send();
69
+ });
70
+ return router;
71
+ }
72
+ exports.default = default_1;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const os_1 = __importDefault(require("os"));
7
+ const kotori_bot_1 = require("kotori-bot");
8
+ const common_1 = require("../../utils/common");
9
+ /* eslint-disable-next-line func-names */
10
+ function default_1(ctx, app) {
11
+ const getModuleData = () => {
12
+ const list = [];
13
+ ctx[kotori_bot_1.Symbols.modules].forEach((module) => list.push(module[0].pkg));
14
+ return list;
15
+ };
16
+ const getBotData = () => {
17
+ const list = [];
18
+ ctx[kotori_bot_1.Symbols.bot].forEach((bot) => bot.forEach((api) => list.push({
19
+ status: api.adapter.status,
20
+ platform: api.adapter.platform,
21
+ identity: api.adapter.identity,
22
+ id: String(api.adapter.selfId),
23
+ lang: api.adapter.config.lang
24
+ })));
25
+ return list;
26
+ };
27
+ const router = app.router();
28
+ router.get('/modules/:scope?/:name?', (req, res) => {
29
+ const { scope, name } = req.params;
30
+ if (!scope)
31
+ return res.json(getModuleData());
32
+ const moduleName = name ? `${scope}/${name}` : scope;
33
+ const moduleData = getModuleData().find((module) => module.name === moduleName);
34
+ return moduleData ? res.json(moduleData) : res.status(404).json({ message: 'Modules not found' });
35
+ });
36
+ router.get('/bots/:name', (req, res) => {
37
+ const { name } = req.params;
38
+ if (!name)
39
+ return res.json(getBotData());
40
+ const botData = getBotData().find((bot) => bot.identity === name);
41
+ return botData ? res.json(botData) : res.status(404).json({ message: 'Bot not found' });
42
+ });
43
+ router.get('/stats', async (_, res) => {
44
+ const botsStatus = getBotData().map((bot) => bot.status.value === 'online');
45
+ const msgTotal = (0, common_1.calcGrandRecord)(ctx.webui.getMsgTotal().origin);
46
+ const { success: loginSuccess, failed: loginFailed } = ctx.webui.getLoginStats();
47
+ const chats = { received: [], sent: [] };
48
+ [0, 1, 2, 3, 4, 5, 6, 7].forEach((day) => {
49
+ const { received, sent } = (0, common_1.calcGrandRecord)(ctx.webui.getMsgDay(day).origin);
50
+ chats.received.push(received || 0);
51
+ chats.sent.push(sent || 0);
52
+ });
53
+ res.json({
54
+ chats,
55
+ count: {
56
+ midwares: ctx[kotori_bot_1.Symbols.midware].size,
57
+ commands: ctx[kotori_bot_1.Symbols.command].size,
58
+ regexps: ctx[kotori_bot_1.Symbols.regexp].size,
59
+ bots: ctx[kotori_bot_1.Symbols.bot].size,
60
+ adapters: ctx[kotori_bot_1.Symbols.adapter].size,
61
+ modules: ctx[kotori_bot_1.Symbols.modules].size
62
+ },
63
+ system: {
64
+ type: os_1.default.type(),
65
+ arch: os_1.default.arch(),
66
+ uptime: os_1.default.uptime(),
67
+ hostname: os_1.default.hostname(),
68
+ homedir: os_1.default.homedir(),
69
+ node: process.version
70
+ },
71
+ info: [
72
+ { name: '累计消息收:发量', value: `${msgTotal.received}:${msgTotal.sent}` },
73
+ { name: '实例在线数/总数', value: `${botsStatus.filter((status) => status).length}/${botsStatus.length}` },
74
+ { name: '登录成功:失败次数', value: `${loginSuccess}:${loginFailed}` },
75
+ { name: '内存使用量', value: `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)} MB` }
76
+ ]
77
+ });
78
+ });
79
+ router.get('/status', (_, res) => {
80
+ res.json({
81
+ ...ctx.webui.getStats(),
82
+ mode: ctx.options.mode,
83
+ core: ctx.pkg.version
84
+ });
85
+ });
86
+ return router;
87
+ }
88
+ exports.default = default_1;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* eslint-disable-next-line func-names */
4
+ function default_1(ctx, app) {
5
+ const router = app.router();
6
+ return router;
7
+ }
8
+ exports.default = default_1;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const router_1 = __importDefault(require("./router"));
7
+ /* eslint-disable-next-line func-names */
8
+ function default_1(ctx, app) {
9
+ const router = app.router();
10
+ router_1.default.forEach((page) => {
11
+ router.use(page.path, page.handler(ctx, app));
12
+ });
13
+ return router;
14
+ }
15
+ exports.default = default_1;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const accounts_1 = __importDefault(require("./api/accounts"));
7
+ const config_1 = __importDefault(require("./api/config"));
8
+ const data_1 = __importDefault(require("./api/data"));
9
+ function defineRouter(config) {
10
+ return config;
11
+ }
12
+ exports.default = defineRouter([
13
+ {
14
+ path: '/api/accounts',
15
+ handler: accounts_1.default
16
+ },
17
+ {
18
+ path: '/api/config',
19
+ handler: config_1.default
20
+ },
21
+ {
22
+ path: '/api/data',
23
+ handler: data_1.default
24
+ }
25
+ ]);
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Context = void 0;
4
+ var kotori_bot_1 = require("kotori-bot");
5
+ Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return kotori_bot_1.Context; } });
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.calcGrandRecord = exports.getDate = exports.generateMessage = exports.getCpuData = exports.getRamData = exports.generateToken = void 0;
7
+ const kotori_bot_1 = require("kotori-bot");
8
+ const os_1 = __importDefault(require("os"));
9
+ function generateToken() {
10
+ return (0, kotori_bot_1.getUuid)().replaceAll('-', '');
11
+ }
12
+ exports.generateToken = generateToken;
13
+ function getRamData() {
14
+ const total = os_1.default.totalmem() / 1024 / 1024 / 1024;
15
+ const unused = os_1.default.freemem() / 1024 / 1024 / 1024;
16
+ const used = total - unused;
17
+ const rate = (used / total) * 100;
18
+ return { total, unused, used, rate };
19
+ }
20
+ exports.getRamData = getRamData;
21
+ function getCpuData() {
22
+ const cpuData = os_1.default.cpus();
23
+ let rate = 0;
24
+ let speed = 0;
25
+ cpuData.forEach((value) => {
26
+ const { times, speed: spd } = value;
27
+ rate += (1 - times.idle / (times.idle + times.user + times.nice + times.sys + times.irq)) * 100;
28
+ speed += spd / cpuData.length;
29
+ });
30
+ return { rate, speed };
31
+ }
32
+ exports.getCpuData = getCpuData;
33
+ function generateMessage(type, data) {
34
+ return JSON.stringify(type === 'error' ? { type, message: data } : { type, data });
35
+ }
36
+ exports.generateMessage = generateMessage;
37
+ function getDate() {
38
+ return `${new Date().getFullYear()}-${new Date().getMonth() + 1}-${new Date().getDay()}`;
39
+ }
40
+ exports.getDate = getDate;
41
+ function calcGrandRecord(data) {
42
+ return Object.values(data).reduce((acc, cur) => ({
43
+ received: acc.received + cur.received,
44
+ sent: acc.sent + cur.sent
45
+ }), { received: 0, sent: 0 });
46
+ }
47
+ exports.calcGrandRecord = calcGrandRecord;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ require("../types");
27
+ const kotori_bot_1 = __importStar(require("kotori-bot"));
28
+ class WebuiTransport extends kotori_bot_1.Transport {
29
+ handle(data) {
30
+ (0, kotori_bot_1.none)(this);
31
+ kotori_bot_1.default.emit('console_output', data);
32
+ }
33
+ }
34
+ exports.default = WebuiTransport;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Webui = exports.config = void 0;
7
+ const date_fns_1 = require("date-fns");
8
+ const kotori_bot_1 = require("kotori-bot");
9
+ const common_1 = require("./common");
10
+ const constant_1 = require("../constant");
11
+ const transport_1 = __importDefault(require("./transport"));
12
+ exports.config = kotori_bot_1.Tsu.Object({
13
+ username: kotori_bot_1.Tsu.String().default(constant_1.DEFAULT_USERNAME),
14
+ password: kotori_bot_1.Tsu.String().default(constant_1.DEFAULT_PASSWORD),
15
+ interval: kotori_bot_1.Tsu.Number().default(5)
16
+ });
17
+ class Webui extends kotori_bot_1.Service {
18
+ timer;
19
+ constructor(ctx, cfg) {
20
+ super(ctx, cfg, 'webui');
21
+ }
22
+ start() {
23
+ /* Add webui transport to logger */
24
+ const logger = this.ctx.get('logger');
25
+ logger.options.transports.push(new transport_1.default({}));
26
+ /* Load records from file to cache */
27
+ const msgTotal = this.ctx.file.load(`${"msg-total" /* KEY.MSG_TOTAL */}.json`, 'json', {});
28
+ const msgDay = this.ctx.file.load(`${"msg-day" /* KEY.MSG_DAY */}${(0, common_1.getDate)()}.json`, 'json', {});
29
+ this.setMsgTotal({ origin: msgTotal });
30
+ this.setMsgDay({ day: (0, common_1.getDate)(), origin: msgDay });
31
+ /* Auto save records from cache to file */
32
+ this.timer = setInterval(() => {
33
+ this.ctx.file.save(`${"msg-total" /* KEY.MSG_TOTAL */}.json`, this.getMsgTotal().origin, 'json');
34
+ const msgDay = this.getMsgDay();
35
+ if (msgDay.day === (0, common_1.getDate)()) {
36
+ this.ctx.file.save(`${"msg-day" /* KEY.MSG_DAY */}${(0, common_1.getDate)()}.json`, msgDay.origin, 'json');
37
+ }
38
+ else {
39
+ this.setMsgDay({ day: (0, common_1.getDate)(), origin: {} });
40
+ }
41
+ }, this.config.interval * 1000);
42
+ /* Update records */
43
+ this.ctx.on('send', (data) => {
44
+ const { identity } = data.api.adapter;
45
+ const { origin: dataTotal } = this.getMsgTotal();
46
+ const { origin: dataDay } = this.getMsgDay();
47
+ if (!(identity in dataTotal))
48
+ dataTotal[identity] = { sent: 0, received: 0 };
49
+ if (!(identity in dataDay))
50
+ dataDay[identity] = { sent: 0, received: 0 };
51
+ dataTotal[identity].sent = (dataTotal[identity].sent || 0) + 1;
52
+ dataDay[identity].sent = (dataDay[identity].sent || 0) + 1;
53
+ });
54
+ this.ctx.midware((next, session) => {
55
+ next();
56
+ const { identity } = session.api.adapter;
57
+ const { origin: dataTotal } = this.getMsgTotal();
58
+ const { origin: dataDay } = this.getMsgDay();
59
+ if (!(identity in dataTotal))
60
+ dataTotal[identity] = { sent: 0, received: 0 };
61
+ if (!(identity in dataDay))
62
+ dataDay[identity] = { sent: 0, received: 0 };
63
+ dataTotal[identity].received = (dataTotal[identity].received || 0) + 1;
64
+ dataDay[identity].received = (dataDay[identity].received || 0) + 1;
65
+ }, 10);
66
+ }
67
+ getToken() {
68
+ return this.ctx.file.load('token', 'txt');
69
+ }
70
+ updateToken() {
71
+ return this.ctx.file.save('token', (0, common_1.generateToken)(), 'txt');
72
+ }
73
+ checkToken(token) {
74
+ return token && token === `Bearer ${this.getToken()}`;
75
+ }
76
+ getStats() {
77
+ (0, kotori_bot_1.none)(this);
78
+ return {
79
+ ram: (0, common_1.getRamData)(),
80
+ cpu: (0, common_1.getCpuData)()
81
+ };
82
+ }
83
+ getMsgTotal() {
84
+ return this.ctx.cache.get("msg-total" /* KEY.MSG_TOTAL */);
85
+ }
86
+ setMsgTotal(data) {
87
+ this.ctx.cache.set("msg-total" /* KEY.MSG_TOTAL */, data);
88
+ }
89
+ getMsgDay(days = 0) {
90
+ if (!days)
91
+ return this.ctx.cache.get("msg-day" /* KEY.MSG_DAY */);
92
+ const dateString = (0, date_fns_1.format)((0, date_fns_1.addDays)(new Date(), -days), 'yyyy-M-d');
93
+ return {
94
+ day: dateString,
95
+ origin: this.ctx.file.load(`${"msg-day" /* KEY.MSG_DAY */}${dateString}.json`, 'json', {
96
+ sent: 0,
97
+ received: 0
98
+ })
99
+ };
100
+ }
101
+ setMsgDay(data) {
102
+ this.ctx.cache.set("msg-day" /* KEY.MSG_DAY */, data);
103
+ }
104
+ getLoginStats() {
105
+ return this.ctx.file.load(`${"login-stats" /* KEY.LOGIN_STATS */}.json`, 'json', { success: 0, failed: 0 });
106
+ }
107
+ setLoginStats(stats) {
108
+ this.ctx.file.save(`${"login-stats" /* KEY.LOGIN_STATS */}.json`, stats, 'json');
109
+ }
110
+ }
111
+ exports.Webui = Webui;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("../types");
4
+ const common_1 = require("../utils/common");
5
+ function main(ctx, server) {
6
+ server.on('connection', (ws) => {
7
+ // if (!ctx.webui.checkToken(req.headers.authorization)) ws.close(1008, 'Invalid token');
8
+ /* Setup interval to send stats to client */
9
+ const interval = ctx.webui.config.interval * 1000;
10
+ const timer = setInterval(() => {
11
+ if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) {
12
+ clearInterval(timer);
13
+ return;
14
+ }
15
+ ws.send((0, common_1.generateMessage)('stats', ctx.webui.getStats()));
16
+ }, interval);
17
+ ctx.on('dispose', () => clearInterval(timer));
18
+ /* Listen for messages from client */
19
+ ws.on('message', (message) => {
20
+ let data;
21
+ try {
22
+ data = JSON.parse(message.toString());
23
+ }
24
+ catch {
25
+ return;
26
+ }
27
+ switch (data.action) {
28
+ case 'command':
29
+ if (!data.command) {
30
+ ws.send((0, common_1.generateMessage)('error', 'No command provided.'));
31
+ break;
32
+ }
33
+ process.stdin.emit('data', data.command);
34
+ ctx.logger.label('server').trace(`Received command from client: ${data.command}`);
35
+ break;
36
+ default:
37
+ }
38
+ });
39
+ /* Listen for console output from server */
40
+ ctx.on('console_output', (data) => ws.send((0, common_1.generateMessage)('console_output', data)));
41
+ });
42
+ }
43
+ exports.default = main;