import { Plugin, Configurable, EventListener, DependencyLoad } from '@squeebot/core/lib/plugin'; import { IMessage, MessageResolver } from '@squeebot/core/lib/types'; import { query, QueryResult } from 'gamedig'; interface IMinecraftType { vanilla: { raw: { description: any, players: { max: number, online: number; }, version: { name: string; } } }; } interface ICommonResponse { name: string; connect: string; description: string; players: string[]; maxPlayers: number; version: string; } /** * Find a string from a random mash of objects.. * @param obj Object * @returns found string or undefined */ function traverseFindString(obj: any): string | undefined { if (typeof obj === 'string') { return obj; } if (Array.isArray(obj)) { obj = obj.map((v) => traverseFindString(v)); for (const p of obj) { if (typeof p === 'string') { return p; } } return; } for (const k of Object.keys(obj)) { const find = traverseFindString(obj[k]); if (find) { return find; } } return; } class Parsers { public static parseMinecraftStatus(response: QueryResult): ICommonResponse | null { const result: ICommonResponse = { name: 'Minecraft', connect: response.connect, description: '', players: [], maxPlayers: 0, version: '' }; result.players = response.players.map((player) => player.name) as string[]; const raw = response.raw as IMinecraftType; if (!raw) { return null; } const desc = traverseFindString(raw.vanilla.raw.description) || ''; result.description = desc.replace(/ยง./g, '').replace(/\n/g, ' '); result.maxPlayers = response.maxplayers; result.version = raw.vanilla.raw.version.name; return result; } } @Configurable({ games: [] }) class GamePlugin extends Plugin { @EventListener('pluginUnload') public unloadEventHandler(plugin: string | Plugin): void { if (plugin === this.name || plugin === this) { this.emit('pluginUnloaded', this); } } @DependencyLoad('simplecommands') addCommands(cmd: any): void { for (const i in this.config.get('games', [])) { const game = this.config.get('games', [])[i]; if (game.game === 'minecraft' && game.host) { const port = game.port || 25565; const command: any = { plugin: this.name, name: 'minecraft', execute: async (msg: IMessage, msr: MessageResolver, spec: any, prefix: string, ...simplified: any[]): Promise => { const keys: any[] = []; let inclPlayers = false; if (simplified[0] === 'players' || simplified[0] === 'online') { inclPlayers = true; } keys.push(['field', 'Minecraft', { type: 'title' }]); let state; let parsed; try { state = await query({ type: 'minecraftping', host: game.host, port, }); parsed = Parsers.parseMinecraftStatus(state); if (!state || !parsed) { throw new Error(); } } catch (e) { console.log(e) msg.resolve([['field', 'Server is offline.', { type: 'title' }]]); return true; } if (inclPlayers) { keys.push(['field', parsed.players.length ? parsed.players.join(', ') : 'No players', { label: 'Players online' }]); msg.resolve(keys); return true; } keys.push(['field', parsed.connect, { label: 'Address' }]); keys.push(['field', parsed.description, { label: 'MOTD', type: 'description' }]); keys.push(['field', parsed.players.length + '/' + parsed.maxPlayers, { label: 'Players' }]); keys.push(['field', parsed.version, { label: 'Version' }]); msg.resolve(keys); return true; }, description: 'Minecraft server', usage: '[players]', aliases: ['mc'] }; if (game.rooms) { command.source = game.rooms; } cmd.registerCommand(command); } } } } module.exports = GamePlugin;