Simp-O-Matic

Dumb Discord bot in TS.
git clone git://git.knutsen.co/Simp-O-Matic
Log | Files | Refs | README | LICENSE

commit e502583b90f1a0c89ee41688a236112d54b55dce
parent 41cdf09e9d6592ec0da710b6ee0c39613090332c
Author: Bruno <b-coimbra@hotmail.com>
Date:   Wed, 25 Mar 2020 16:00:03 -0300

added cron and hangman commands

Diffstat:
Alib/commands/cron.ts | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/commands/hangman.ts | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/default.ts | 2+-
Mlib/extensions.ts | 1+
4 files changed, 330 insertions(+), 1 deletion(-)

diff --git a/lib/commands/cron.ts b/lib/commands/cron.ts @@ -0,0 +1,133 @@ +interface Schedule { + hours?: string; + minutes?: string; + dayOfMonth?: string; + month?: string; + dayOfWeek?: string; +} + +interface Command { + name: string, + args?: string[] +}; + +interface Cron { + job: number, + schedule?: Schedule, + command?: Command +}; + +const RESPONSES = { + help: { + rm: '.cron rm #[job-index]', + command: ':warning: There is no command to execute the cron job on.', + schedule: ':warning: There is no schedule to execute the command on.' + }, + added: 'New cron job has been added', // TODO + empty: ":warning: There are no cron jobs being executed." +}; + +const MATCHERS = { + hour_mins: + /^(((0|1)[0-9])|2[0-3]):[0-5][0-9]\s?(pm|am)?$/i, + day_of_month: + /(?:\b)(([1-9]|0[1-9]|[1-2][0-9]|3[0-1])(st|nd|rd|th)?)(?:\b|\/)/i, + weekdays: 'mon tue wed thu fri sat sun'.split(' '), + prefix: (prefix: string) => new RegExp(`^${prefix}`), + ordinals: /st|nd|rd|th/, + greenwich: /pm|am/ +}; + +export default (home_scope: HomeScope) => { + const { message, args, CONFIG } = home_scope; + + // let command = `20:32pm 26th 7 fri ${prefix}echo poopoo`; + let crons: Cron[] = CONFIG.cron_jobs; + + const matches = (value: string, regex: RegExp): string | undefined => + (value.match(regex) || {})?.input; + + const rm = (job: number) => + delete crons[crons.map(cron => cron.job).indexOf(job)]; + + // TODO + const list = () => { + if (crons.length == 0) + return message.answer(RESPONSES.empty); + + crons.each(cron => message.answer("Can't list jobs yet...")); + } + + const parse = (args: string[]): Cron => { + let cron: Cron = { + job: crons.slice(-1)[0]?.job + 1 || 0 + }; + + args.some((argument, i) => { + switch (argument) { + case (matches(argument, MATCHERS.prefix(CONFIG.commands.prefix))): + cron.command = { + name: argument, + args: args.slice(i + 1) + }; + break; + case (matches(argument, MATCHERS.hour_mins)): + const [hour, min] = argument.split(':'); + + cron.schedule = { + hours: hour, + minutes: min.split(MATCHERS.greenwich)[0] + }; + break; + case (matches(argument, MATCHERS.day_of_month)): + const date = + matches(argument, MATCHERS.ordinals) == undefined + ? { month: argument } + : { dayOfMonth: argument.split(MATCHERS.ordinals)[0] }; + + cron.schedule = { + ...cron.schedule, + ...date + }; + break; + } + + argument = argument.toLowerCase(); + + if (MATCHERS.weekdays.includes(argument)) { + cron.schedule = { + ...cron.schedule, + dayOfWeek: ( + MATCHERS.weekdays.indexOf(argument) + 1 + ).toString() + }; + } + }); + + return cron; + }; + + let options = args.join(' ').split(' '); + + if (options[0] === 'ls') + list(); + else if (options[0] === 'rm') { + let job: number = Number(options[1]); + + (isNaN(job)) + ? message.answer(RESPONSES.help.rm) + : rm(job); + } + else { + const cron: Cron = parse(options); + + if (!cron?.command) + message.answer(RESPONSES.help.command); + else if (!cron?.schedule) + message.answer(RESPONSES.help.schedule) + else { + crons.push(parse(options)); + message.answer(RESPONSES.added); + } + } +}; diff --git a/lib/commands/hangman.ts b/lib/commands/hangman.ts @@ -0,0 +1,195 @@ +import { Message, User } from 'discord.js'; +import { FORMATS } from '../extensions'; + +namespace Types { + type ScoreStats = { + missed: number, + scored: number + }; + + export type Mask = '_' | '.' | '-' | ':'; + + export interface Score { + [id: string]: ScoreStats + }; + + export interface Guess { + id: string, + answer: string, + attempt?: string + }; + + export interface Config { + attempt_limit: number, + mask: Mask; + }; + + export interface Messages { + help: string, + lose: string, + miss: string, + in_progress: string, + win: (id: string) => string, + start: (word: string) => string, + guess: (attempt: string) => string, + result: (missed: number, + scored: number) => string; + }; + + export enum Status { + Started, + InProgress + } +} + +declare global { + interface Array<T> { + result(): string; + pick(): string; + } + + interface String { + mask_with(mask: Types.Mask): string[]; + } +} + +Array.prototype.result = function () { + return this.join(''); +}; + +Array.prototype.pick = function (): string { + return this[Math.floor(Math.random() * this.length)]; +} + +String.prototype.mask_with = function (mask: Types.Mask = '_') { + return [...(mask.repeat(this.length))]; +}; + +const WORDS: string[] = [ + "hello", + "world" +]; + +let GAME_STATUS: Types.Status; + +let scores: Types.Score = { + "29138138129139128": { + missed: 0, + scored: 0 + } +} + +const CONFIG: Types.Config = { + attempt_limit: 10, + mask: '_' +} + +const MESSAGES: Types.Messages = { + help: "To start a new hangman game" + + ".hangman new\n".format(FORMATS.block) + + "Make a guess: " + + ".hangman [@letter]\n".format(FORMATS.block) + + "Guess the full word: " + + ".hangman [@word]".format(FORMATS.block), + in_progress: "A hangman game is already in progress!", + lose: "You reached the number of attempts. You lose!", + miss: "You missed :(", + start: (word) => + `The word is ${word.length} letters long.\n` + + `Make a guess!`, + win: (id) => + `Congrats ${id}, you won the game!`, + guess: (attempt) => + `You guessed: ${attempt}`, + result: (missed, scored) => + `Missed: ${missed}\n` + + `Scored: ${scored}`, +}; + +// export default (home_scope: HomeScope) => { +// const { message, args } = home_scope; + + // if (args.length === 0 || args[0] === 'help') + // message.channel.send(MESSAGES.help); + +const args = ['new']; + +const start = (w: string) => { + console.log(MESSAGES.start(w)); + GAME_STATUS = Types.Status.Started; +}; + +const answer = WORDS.pick(); + +const [id, attempts, output]: [string, number, string[]] = [ + // message.author, + "494924924924999", + CONFIG.attempt_limit, + answer.mask_with(CONFIG.mask) +]; + +const score = (id: string) => + scores[id].scored++; + +const miss = (id: string) => + scores[id].missed++; + +const guessed = (attempt: string) => + MESSAGES.guess(attempt); + +const hasWon = () => + !output.includes(CONFIG.mask); + +const lose = () => + console.log(MESSAGES.lose); + // message.channel.send(MESSAGES.lose); + +const win = (id: string) => { + score(id); + const { missed, scored } = scores[id]; + MESSAGES.win(id); + MESSAGES.result(missed, scored); +}; + +const guess = ({ id, answer, attempt }: Types.Guess) => { + if (GAME_STATUS != (Types.Status.Started | Types.Status.InProgress)) { + console.log(MESSAGES.in_progress); + return; + } + + guessed(attempt); + + if (attempt.length === answer.length) + (attempt === answer) ? win(id) : miss(id); + + if (answer.indexOf(attempt) != -1) { + miss(id); + + return (scores[id].missed >= attempts) + ? lose() + : output.result(); + } + + [...answer] + .each((letter: string, i: number) => { + if (letter === attempt) + output[i] = attempt.toUpperCase(); + + if (typeof (output[i]) === 'undefined') + output[i] = CONFIG.mask; + }); + + score(id); + return output.result(); +}; + +if (args[0] === 'new') + start(answer); +else { + guess({ id, answer, attempt: args[0] }); + + if (hasWon()) + win(id); +} + +// }; diff --git a/lib/default.ts b/lib/default.ts @@ -11,7 +11,7 @@ const DEFAULT_GUILD_CONFIG : Types.Config = { weather_locations: { '541761315887120399': 'Moscow' }, - + cron_jobs: [], commands: { prefix: '!', max_history: 40, diff --git a/lib/extensions.ts b/lib/extensions.ts @@ -25,6 +25,7 @@ declare global { main_channel: string, system_channel: string, pp_sizes: { [key: string]: number } + cron_jobs: any[], weather_locations: { [key: string]: string }, commands: { prefix: string,