+++ /dev/null
-#chat {
- border: solid 1px #6d251c;
-}
-
-#chat-messages {
- max-height: 250px;
- overflow: auto;
-}
-
-.chat-message {
- line-height: 1.2rem;
- padding: 0.2rem 0.3rem 0.3rem;
-}
-
-.chat-message:nth-child(even) {
- background: linear-gradient(270deg, rgba(0, 0, 0, 0) 0, rgba(196, 177, 149, 0.8) 100%);
-}
-
-.chat-message .from {
- font-weight: bold;
-}
-
-.chat-message .from::after {
- content: ':';
-}
-
-#chat-form {
- display: flex;
-}
-
-#chat-form input {
- flex-grow: 8;
- padding: 0.3rem;
- outline: none;
- border-width: 1px 0 0;
- background: transparent;
-}
-
-#chat-form input:focus {
- outline: none;
-}
-
-#chat-form button {
- border-right-width: 0px;
- border-bottom-width: 0px;
- font-weight: bold;
-}
-
-#chat-form button:active {
- top: 0;
-}
\ No newline at end of file
</div>
<section id="chat">
- <div id="chat-messages" hx-trigger="load delay:1s" hx-get="/chat/history" hx-swap="afterbegin"></div>
- <form id="chat-form" hx-post="/chat">
- <input type="text" id="message" name="message" autocomplete="off"><button type="submit">Send</button>
- </form>
+ <widgetbot server="1116668527684894772" channel="1116668528402104362" width="100%" height="600"></widgetbot>
+ <script src="https://cdn.jsdelivr.net/npm/@widgetbot/html-embed"></script>
</section>
<section id="game-footer">
import {broadcastMessage} from '@shared/message';
import { Player } from '@shared/player';
import {createFight, getMonsterList, getMonsterLocation, getRandomMonster, loadMonster, loadMonsterFromFight} from './monster';
-import { addInventoryItem } from './inventory';
import {FightTrigger, Monster} from '@shared/monsters';
-import {getShopEquipment } from './shopEquipment';
import { getAllPaths, getAllServices, getCityDetails, getService, getTravelPlan, getDungeon } from './map';
import { signup, login, authEndpoint } from './auth';
import {db} from './lib/db';
import { renderMonsterSelector, renderOnlyMonsterSelector } from './views/monster-selector';
import { renderFight, renderFightPreRound, renderRoundDetails } from './views/fight';
import { renderTravel, travelButton } from './views/travel';
-import { renderChatMessage } from './views/chat';
// TEMP!
import { completeDungeonFight, getActiveDungeon, getRoomVists, loadRoom } from './dungeon';
});
socket.emit('authToken', player.id);
-
- socket.emit('chat', renderChatMessage(broadcastMessage('server', `${player.username} just logged in`)));
}
function uniqueConnectedUsers(): Set<string> {
+++ /dev/null
-import { Server, Socket } from 'socket.io';
-import { Player } from '@shared/player';
-import { broadcastMessage } from '@shared/message';
-import { renderChatMessage } from './views/chat';
-import { Commands } from './chat-commands/';
-import { logger } from './lib/logger';
-
-
-export async function handleChatCommands(msg: string, player: Player, io: Server, sender: Socket): Promise<void> {
- const rawCommand = msg.split('/server ')[1];
-
- logger.debug(`${player.username} running command: [${rawCommand}]`);
- let matched = false;
- Commands.forEach(async command => {
- if(command.regex.test(rawCommand)) {
- matched = true;
- console.log(`${player.username} running command: [${rawCommand}]`);
- await command.handler(rawCommand, sender, player, io);
- }
- });
-
- if(!matched) {
- const message = broadcastMessage('server', `Invalid command: [${rawCommand}]`);
- sender.emit('chat', renderChatMessage(message));
- }
-}
+++ /dev/null
-import type { Player } from '../../shared/player';
-import type { Server, Socket } from 'socket.io';
-
-type ChatCommandHandler = (command: string, sender: Socket, player: Player, io: Server) => Promise<void>;
-
-export class ChatCommand {
- constructor(
- public name: string,
- public regex: RegExp,
- public handler: ChatCommandHandler
- ) {
-
- }
-}
+++ /dev/null
-import { Socket } from 'socket.io';
-import { Permission, PermissionGuard, Player } from '../../shared/player';
-import { ChatCommand } from './base';
-import { givePlayerPermission } from '../admin';
-import { findPlayerByUsername } from '../player';
-import { broadcastMessage, Message } from '../../shared/message';
-import { renderChatMessage } from './../views/chat';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('admin')) {
- const pieces = rawCommand.split(' ');
- const username = pieces[1];
- const permission = pieces[2];
-
- if(PermissionGuard(permission)) {
- const recipient = await findPlayerByUsername(username);
- if(player) {
- await givePlayerPermission(recipient.id, permission as Permission);
-
- const message: Message = broadcastMessage('server', `Granted ${permission} to ${recipient.username}`);
- sender.emit('chat', renderChatMessage(message));
- }
- else {
- const message: Message = broadcastMessage('server', `Cant find user [${username}]`);
- sender.emit('chat', renderChatMessage(message));
- }
- }
- }
-}
-
-export const setPermission = new ChatCommand('give-permission', new RegExp(/^give-permission (.*)+/), handler);
+++ /dev/null
-import { ChatCommand } from "./base";
-import { refreshDropTables } from './refresh-droptables';
-import { refreshMonsters } from "./refresh-monsters";
-import { refreshCities } from './refresh-cities';
-import { refreshShops } from './refresh-shops';
-import { setLevel } from './set-level';
-import { say } from './say';
-import { setPermission } from './give-permission';
-
-export const Commands = new Set<ChatCommand>();
-
-Commands.add(refreshMonsters);
-Commands.add(refreshCities);
-Commands.add(refreshShops);
-Commands.add(refreshDropTables);
-Commands.add(setLevel);
-Commands.add(say);
-Commands.add(setPermission);
+++ /dev/null
-import { ChatCommand } from './base';
-import { Socket } from 'socket.io';
-import { createAllCitiesAndLocations } from '../../../seeds/cities';
-import { broadcastMessage } from '../../shared/message';
-import { renderChatMessage } from './../views/chat';
-import { Player } from '../../shared/player';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('admin')) {
- await createAllCitiesAndLocations();
- const message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!');
- sender.emit('chat', renderChatMessage(message));
- }
-}
-
-export const refreshCities = new ChatCommand('refresh-cities', new RegExp(/^refresh-cities/), handler);
+++ /dev/null
-import { ChatCommand } from './base';
-import { Socket } from 'socket.io';
-import { createDropTables } from '../../../seeds/drop-tables';
-import { broadcastMessage } from '../../shared/message';
-import { renderChatMessage } from '../views/chat';
-import { Player } from '../../shared/player';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('admin')) {
- await createDropTables();
- const message = broadcastMessage('server', 'Drop tables refreshed');
- sender.emit('chat', renderChatMessage(message));
- }
-}
-
-export const refreshDropTables = new ChatCommand('refresh-droptables', new RegExp(/^refresh-droptables/), handler);
+++ /dev/null
-import { ChatCommand } from './base';
-import { Socket } from 'socket.io';
-import { createMonsters } from '../../../seeds/monsters';
-import { broadcastMessage } from '../../shared/message';
-import { renderChatMessage } from './../views/chat';
-import { Player } from '../../shared/player';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('admin')) {
- await createMonsters();
- const message = broadcastMessage('server', 'Monsters refreshed!');
- sender.emit('chat', renderChatMessage(message));
- }
-}
-
-export const refreshMonsters = new ChatCommand('refresh-monsters', new RegExp(/^refresh-monsters$/), handler);
+++ /dev/null
-import { ChatCommand } from './base';
-import { Socket } from 'socket.io';
-import { createShopItems, createShopEquipment } from '../../../seeds/shop_items';
-import { broadcastMessage } from '../../shared/message';
-import { renderChatMessage } from './../views/chat';
-import { Player } from '../../shared/player';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('admin')) {
- await createShopItems();
- await createShopEquipment();
- const message = broadcastMessage('server', 'Shop items refreshed!');
- sender.emit('chat', renderChatMessage(message));
-
- }
-}
-
-export const refreshShops = new ChatCommand('refresh-shops', new RegExp(/^refresh-shops/), handler);
+++ /dev/null
-import { ChatCommand } from './base';
-import { Socket } from 'socket.io';
-import { broadcastMessage, Message } from '../../shared/message';
-import { renderChatMessage } from './../views/chat';
-import { Player } from '../../shared/player';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('moderator') || player.permissions.includes('admin')) {
- let message: Message = broadcastMessage('server', rawCommand.split('say ')[1].trim());
- sender.emit('chat', renderChatMessage(message));
- }
-}
-
-export const say = new ChatCommand('say', new RegExp(/^say (.*)+/), handler);
+++ /dev/null
-import { ChatCommand } from './base';
-import { Socket } from 'socket.io';
-import { broadcastMessage, Message } from '../../shared/message';
-import { renderChatMessage } from './../views/chat';
-import { updatePlayer } from '../player';
-import { maxHp, maxVigor, Player } from '../../shared/player';
-
-async function handler(rawCommand: string, sender: Socket, player: Player) {
- if(player.permissions.includes('admin') || player.permissions.includes('tester')) {
- let message: Message;
- // command in set-level username level
- const pieces = rawCommand.split(' ');
- if(pieces.length !== 2) {
- message = broadcastMessage('server', 'format: /set-level level');
- }
- else {
- const level = parseInt(pieces.pop() || '0');
- if(level < 1) {
- message = broadcastMessage('server', 'format: /set-level [level >= 1]');
- }
- else {
- message = broadcastMessage('server', `Set to level ${level}. Please reload.`);
-
- player.level = level;
- player.strength = 4;
- player.constitution = 4;
- player.dexterity = 4;
- player.intelligence = 4;
- player.hp = maxHp(player.constitution, player.level);
- player.vigor = maxVigor(player.constitution, player.level);
- player.stat_points = level-1;
-
- await updatePlayer(player);
- }
- }
-
- sender.emit('chat', renderChatMessage(message));
-
- }
-}
-
-export const setLevel = new ChatCommand('set-level', new RegExp(/^set-level \d+$/), handler);
+++ /dev/null
-import { Request, Response, Router } from 'express';
-import { broadcastMessage, Message } from '../../shared/message';
-import { authEndpoint } from '../auth';
-import { renderChatMessage } from '../views/chat';
-import { handleChatCommands } from '../chat-commands';
-import xss from 'xss';
-import { logger } from '../lib/logger';
-export const chatRouter = Router();
-
-const chatHistory: Message[] = [];
-
-chatRouter.get('/chat/history', authEndpoint, (req: Request, res: Response) => {
- let html = chatHistory.map(renderChatMessage);
-
- res.send(html.join("\n"));
-});
-
-chatRouter.post('/chat', authEndpoint, async (req: Request, res: Response) => {
- const msg = req.body.message.trim();
-
- if(!msg || !msg.length) {
- res.sendStatus(204);
- return;
- }
-
- logger.debug(`${req.player.username} sending message: [${msg}] (${req.player.permissions.join(', ')})`);
-
- if(msg.startsWith('/server') && req.player.permissions.includes('admin')) {
- const sender = req.rl.io.sockets.sockets.get(req.rl.cache.get(`socket:${req.player.id}`));
- try {
- await handleChatCommands(msg, req.player, req.rl.io, sender);
- }
- catch(e) {
- sender.emit('chat', renderChatMessage(broadcastMessage('server', e.message)));
- }
- }
- else if(msg === '/online') {
- const uniqueConnectedUsers = new Set<string>();
-
- req.rl.io.sockets.sockets.forEach((socket) => {
- uniqueConnectedUsers.add(req.rl.cache.get(`socket-lookup:${socket.id}`).username);
- });
-
- const users = Array.from(uniqueConnectedUsers.values());
- // send to specific user
- const message = broadcastMessage('server', `Online Players: [${users.join(", ")}]`);
- req.rl.io.to(req.rl.cache.get(`socket:${req.player.id}`)).emit('chat', renderChatMessage(message));
- res.sendStatus(204);
- }
- else {
- const message = broadcastMessage(req.player.username, xss(msg, {
- whiteList: {}
- }));
- chatHistory.push(message);
- chatHistory.slice(-10);
- req.rl.io.emit('chat', renderChatMessage(message));
- }
-
- res.sendStatus(204);
-});
-export { chatRouter } from './chat';
export { inventoryRouter } from './inventory';
export { profileRouter } from './profile';
export { travelRouter } from './travel';
+++ /dev/null
-import {Message} from "@shared/message";
-
-export function renderChatMessage(msg: Message): string {
- return `<div class="chat-message" title="${new Date(parseInt(msg.sentAt))}" id="${msg.id}">
- <span class="from">${msg.from}</span>
- <span class="message">${msg.msg}</span>
- </div>`;
-}