cool color and format utilities
This commit is contained in:
parent
2b027d57ad
commit
d11a983319
@ -1,4 +1,8 @@
|
|||||||
import { IRCBot } from '../bot';
|
import { IRCBot } from '../bot';
|
||||||
|
import {
|
||||||
|
applyTextColor,
|
||||||
|
wrapFormattedText,
|
||||||
|
} from '../utility/message-formatting';
|
||||||
import { NickServValidator } from '../utility/nickserv-validator';
|
import { NickServValidator } from '../utility/nickserv-validator';
|
||||||
|
|
||||||
const bot = new IRCBot({
|
const bot = new IRCBot({
|
||||||
@ -29,7 +33,7 @@ bot.on('supported-modes', (supported) => {
|
|||||||
// bot.on('line', console.log);
|
// bot.on('line', console.log);
|
||||||
|
|
||||||
bot.on('message', ({ message, to, nickname }) => {
|
bot.on('message', ({ message, to, nickname }) => {
|
||||||
console.log(`[${to}] ${nickname}: ${message}`);
|
console.log(`[${to}] ${nickname}: ${wrapFormattedText(message)}`);
|
||||||
|
|
||||||
if (message.startsWith('!test')) {
|
if (message.startsWith('!test')) {
|
||||||
nickserv
|
nickserv
|
||||||
@ -46,6 +50,11 @@ bot.on('message', ({ message, to, nickname }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.startsWith('!colors')) {
|
||||||
|
bot.send(to, `${applyTextColor('blue', 'cool blue text!')}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.startsWith('!whois')) {
|
if (message.startsWith('!whois')) {
|
||||||
bot.whois(nickname).then(console.log);
|
bot.whois(nickname).then(console.log);
|
||||||
return;
|
return;
|
||||||
|
@ -5,6 +5,7 @@ export * from './types/impl.interface';
|
|||||||
export * from './utility/collector';
|
export * from './utility/collector';
|
||||||
export * from './utility/estimate-prefix';
|
export * from './utility/estimate-prefix';
|
||||||
export * from './utility/formatstr';
|
export * from './utility/formatstr';
|
||||||
|
export * from './utility/message-formatting';
|
||||||
export * from './utility/mode-from-prefix';
|
export * from './utility/mode-from-prefix';
|
||||||
export * from './utility/nickserv-validator';
|
export * from './utility/nickserv-validator';
|
||||||
export * from './utility/parser';
|
export * from './utility/parser';
|
||||||
|
@ -14,7 +14,7 @@ export type IRCCommunicatorEvents = {
|
|||||||
/**
|
/**
|
||||||
* Supported channel user modes from the server (e.g. `ohv: @%+`)
|
* Supported channel user modes from the server (e.g. `ohv: @%+`)
|
||||||
*/
|
*/
|
||||||
'supported-modes': (modes: Record<string, unknown>) => void;
|
'supported-modes': (modes: Record<string, string>) => void;
|
||||||
/**
|
/**
|
||||||
* Everything this server supports. See IRC documentation for command `005` or `RPL_ISUPPORT` for more info.
|
* Everything this server supports. See IRC documentation for command `005` or `RPL_ISUPPORT` for more info.
|
||||||
*/
|
*/
|
||||||
|
216
src/utility/message-formatting.ts
Normal file
216
src/utility/message-formatting.ts
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
export const IRC_FMT_COLOR_MAP = {
|
||||||
|
/**
|
||||||
|
* IRC white `rgb(255,255,255)`
|
||||||
|
*/
|
||||||
|
white: '\u00030',
|
||||||
|
/**
|
||||||
|
* IRC black `rgb(0,0,0)`
|
||||||
|
*/
|
||||||
|
black: '\u00031',
|
||||||
|
/**
|
||||||
|
* IRC blue `rgb(0,0,127)`
|
||||||
|
*/
|
||||||
|
blue: '\u00032',
|
||||||
|
/**
|
||||||
|
* IRC green `rgb(0,147,0)`
|
||||||
|
*/
|
||||||
|
green: '\u00033',
|
||||||
|
/**
|
||||||
|
* IRC red `rgb(255,0,0)`
|
||||||
|
*/
|
||||||
|
red: '\u00034',
|
||||||
|
/**
|
||||||
|
* IRC brown `rgb(127,0,0)`
|
||||||
|
*/
|
||||||
|
brown: '\u00035',
|
||||||
|
/**
|
||||||
|
* IRC purple `rgb(156,0,156)`
|
||||||
|
*/
|
||||||
|
purple: '\u00036',
|
||||||
|
/**
|
||||||
|
* IRC orange `rgb(252,127,0)`
|
||||||
|
*/
|
||||||
|
orange: '\u00037',
|
||||||
|
/**
|
||||||
|
* IRC yellow `rgb(255,255,0)`
|
||||||
|
*/
|
||||||
|
yellow: '\u00038',
|
||||||
|
/**
|
||||||
|
* IRC light green `rgb(0,252,0)`
|
||||||
|
*/
|
||||||
|
lightgreen: '\u00039',
|
||||||
|
/**
|
||||||
|
* IRC cyan `rgb(0,147,147)`
|
||||||
|
*/
|
||||||
|
cyan: '\u000310',
|
||||||
|
/**
|
||||||
|
* IRC light cyan `rgb(0,255,255)`
|
||||||
|
*/
|
||||||
|
lightcyan: '\u000311',
|
||||||
|
/**
|
||||||
|
* IRC light blue `rgb(0,0,252)`
|
||||||
|
*/
|
||||||
|
lightblue: '\u000312',
|
||||||
|
/**
|
||||||
|
* IRC pink `rgb(255,0,255)`
|
||||||
|
*/
|
||||||
|
pink: '\u000313',
|
||||||
|
/**
|
||||||
|
* IRC grey `rgb(127,127,127)`
|
||||||
|
*/
|
||||||
|
grey: '\u000314',
|
||||||
|
/**
|
||||||
|
* IRC light grey `rgb(210,210,210)`
|
||||||
|
*/
|
||||||
|
lightgrey: '\u000315',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ^O character, color reset character
|
||||||
|
*/
|
||||||
|
export const IRC_FMT_RESET = '\u000F';
|
||||||
|
|
||||||
|
export const IRC_FMT_FORMAT_MAP = {
|
||||||
|
/**
|
||||||
|
* Normal text
|
||||||
|
*/
|
||||||
|
normal: '\u0000',
|
||||||
|
/**
|
||||||
|
* <ins>underline text</ins>, `^_`
|
||||||
|
*/
|
||||||
|
underline: '\u001F',
|
||||||
|
/**
|
||||||
|
* **bold text**, `^B`
|
||||||
|
*/
|
||||||
|
bold: '\u0002',
|
||||||
|
/**
|
||||||
|
* *italic text*, `^I`
|
||||||
|
*/
|
||||||
|
italics: '\u001D',
|
||||||
|
/**
|
||||||
|
* reverse color text, `^V`
|
||||||
|
*/
|
||||||
|
reverse: '\u0016',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove color and format characters from a string.
|
||||||
|
* @param str Text
|
||||||
|
* @returns Stripped text
|
||||||
|
*/
|
||||||
|
export const stripFormatting = (str: string): string =>
|
||||||
|
str.replace(/(\x03\d{0,2}(,\d{0,2})?)/g, '').replace(/[\x00-\x1F]/g, '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Colorize a string for sending.
|
||||||
|
* @param color IRC color key
|
||||||
|
* @param str Text to colorize
|
||||||
|
* @returns Text with control characters
|
||||||
|
*/
|
||||||
|
export const applyTextColor = (
|
||||||
|
color: keyof typeof IRC_FMT_COLOR_MAP,
|
||||||
|
str: string,
|
||||||
|
): string =>
|
||||||
|
!IRC_FMT_COLOR_MAP[color]
|
||||||
|
? str
|
||||||
|
: `${IRC_FMT_COLOR_MAP[color]}${str}${IRC_FMT_RESET}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply text formatting to a string for sending.
|
||||||
|
* @param format IRC format key
|
||||||
|
* @param str Text to format
|
||||||
|
* @returns Text with control characters
|
||||||
|
*/
|
||||||
|
export const applyTextFormat = (
|
||||||
|
format: keyof typeof IRC_FMT_FORMAT_MAP,
|
||||||
|
str: string,
|
||||||
|
): string =>
|
||||||
|
!IRC_FMT_FORMAT_MAP[format]
|
||||||
|
? str
|
||||||
|
: `${IRC_FMT_FORMAT_MAP[format]}${str}${IRC_FMT_RESET}`;
|
||||||
|
|
||||||
|
const styleCheckRe = /[\x00-\x1F]/;
|
||||||
|
const backRe = /^(\d{1,2})(,(\d{1,2}))?/;
|
||||||
|
const colourKey = '\x03';
|
||||||
|
const colourRe = /\x03/g;
|
||||||
|
const colorKeys = Object.keys(IRC_FMT_COLOR_MAP);
|
||||||
|
const fmtRegex: [string, RegExp][] = Object.keys(IRC_FMT_FORMAT_MAP).map(
|
||||||
|
(key) => {
|
||||||
|
const escaped = encodeURI(
|
||||||
|
(IRC_FMT_FORMAT_MAP as Record<string, string>)[key],
|
||||||
|
).replace('%', '\\x');
|
||||||
|
return [key, new RegExp(escaped + '(.*?)(' + escaped + '|$)')];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export type IRCTextFormatWrapperFn = (
|
||||||
|
text: string,
|
||||||
|
foregroundColor?: string,
|
||||||
|
backgroundColor?: string,
|
||||||
|
textFormat?: string,
|
||||||
|
) => string;
|
||||||
|
|
||||||
|
const wrap: IRCTextFormatWrapperFn = (text, fg, bg, fmt) =>
|
||||||
|
`<format${fg ? ` color="${fg}"` : ''}${bg ? ` background="${bg}"` : ''}${
|
||||||
|
fmt ? ` text="${fmt}"` : ''
|
||||||
|
}>${text}</format>`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap regions of text that have formatting or colors with a custom wrapper, e.g. HTML.
|
||||||
|
* Defaults to a psuedo-XML style wrapper `<format color background text />`
|
||||||
|
* @param line Text with control characters
|
||||||
|
* @param wrapperFn Wrapper function to use
|
||||||
|
* @returns Text with applied wrappers
|
||||||
|
*/
|
||||||
|
export function wrapFormattedText(line: string, wrapperFn = wrap): string {
|
||||||
|
// Recheck
|
||||||
|
if (!styleCheckRe.test(line)) return line;
|
||||||
|
|
||||||
|
// split up by the irc style break character ^O
|
||||||
|
if (line.indexOf(IRC_FMT_RESET) >= 0) {
|
||||||
|
return line
|
||||||
|
.split(IRC_FMT_RESET)
|
||||||
|
.map((value) => wrapFormattedText(value, wrapperFn))
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = line;
|
||||||
|
const parseArr = result.split(colourKey);
|
||||||
|
|
||||||
|
for (let i = 0; i < parseArr.length; i++) {
|
||||||
|
const text = parseArr[i];
|
||||||
|
const match = text.match(backRe);
|
||||||
|
const colour = match && colorKeys[+match[1]];
|
||||||
|
let background = '';
|
||||||
|
|
||||||
|
if (!match || !colour) {
|
||||||
|
// ^C (no colour) ending. Escape current colour and carry on
|
||||||
|
background = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the background colour
|
||||||
|
// we don't override the background local var to support nesting
|
||||||
|
if (colorKeys[+match[3]]) {
|
||||||
|
background = colorKeys[+match[3]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the parsed text result
|
||||||
|
result = result.replace(
|
||||||
|
colourKey + text,
|
||||||
|
wrapperFn(text.slice(match[0].length), colour, background),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matching styles (italics/bold/underline)
|
||||||
|
// if only colors were this easy...
|
||||||
|
fmtRegex.forEach(function ([style, keyregex]) {
|
||||||
|
if (result.indexOf(style) < 0) return;
|
||||||
|
result = result.replace(keyregex, (match, text) =>
|
||||||
|
wrapperFn(text, undefined, undefined, style),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// replace the reminent colour terminations and be done with it
|
||||||
|
return result.replace(colourRe, '');
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user