Простейший генератор креативного текста
Без нейросетей, регистрации и СМС
Настало время крутых проектов!
Представьте, что вы запускаете рассылку в крупной компании, получили уже за это много денег и вам хочется как-то её разнообразить. И вы решаете сделать так, чтобы человеку в ответ приходило письмо-благодарность. Но не одно письмо на всех, а несколько разных, чем больше — тем лучше.
Можно их все написать вручную и потратить на это несколько дней. А можно написать код, который сам напишет все эти письма. Мы всегда выбираем второй путь, даже если это не нужно, поэтому сегодня мы запрограммируем генератор текста на JavaScript.
Сразу оговоримся: вам может показаться, что этот генератор плохо работает и выдаёт дурацкие тексты. Всё дело в том, что в этом алгоритме важно предусмотреть все варианты составления структуры текста и самих предложений, а потом проверить, как это всё согласовывается между собой. Чем длиннее и «живее» нужен текст, тем больше вариантов подстановки слов нужно предусмотреть.
Алгоритм работы генератора текста будет такой:
- Готовим HTML-страницу, где это всё будет работать и запускаться.
- Задаём структуру текста на уровне абзацев: приветствие, само письмо, заключение.
- Пишем шаблоны для каждого из этих элементов.
- Случайным образом выбираем, каким тоном будем общаться — официальным или нет.
- Собираем всё письмо в одно целое.
- Заменяем шаблонные слова на нужные фразы и словосочетания.
- Показываем результат.
Делаем HTML-страницу
Берём за основу стандартный шаблон и добавляем в него:
- jQuery, чтобы обращаться к элементам страницы по имени;
- файл скрипта mail.js, в котором будет происходить вся магия JS-кода;
- стили для элементов страницы, чтобы она выглядела опрятно;
- блок, куда будем выводить готовое письмо;
- кнопку, которая по нажатию будет генерировать новый текст.
Всё это мы уже умеем делать, поэтому просто собираем код:
<!DOCTYPE html>
<html>
<!-- служебная часть -->
<head>
<!-- заголовок страницы -->
<title>Генератор писем</title>
<!-- настраиваем служебную информацию для браузеров -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- подключаем jQuery -->
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js'></script>
<!-- подключаем наш скрипт -->
<script src="mail.js"> </script>
<!-- настраиваем внешний вид блока для готового письма и кнопки -->
<style type="text/css">
.text{font-size: 20px; width:80%; margin:30px; }
.controls button {font-size: 20px; margin-left: 30px;}
</style>
<!-- закрываем служебную часть страницы -->
</head>
<body>
<!-- в этом блоке будем выводить готовое письмо -->
<div class="text" id="text_here"></div>
<!-- блок с кнопкой, по нажатию на которую запускается нужная функция в скрипте -->
<div class="controls">
<button value="Получить новый текст" onclick="get_text();">Получить новый текст</button>
</div>
</body>
<!-- конец всей страницы -->
</html>
Готовим структуру письма и шаблоны для текста
Теперь нам понадобится отдельный файл для скрипта, который и будет отвечать за генерацию писем — mail.js. Создаём его в той же папке, что и HTML-документ, и начинаем программировать.
Смысл нашего алгоритма в том, что мы заранее определяем блоки текста и порядок фраз, но случайным образом выбираем, что же будет подставляться на место этих блоков.
Для этого мы будем использовать шаблонные слова, у которых с обеих сторон стоят знаки доллара, например, $somebody$. Эти шаблонные слова будут означать для нашего алгоритма, что на их месте появится какое-то конкретное слово или целое предложение. Это мы сами решили, что шаблоны будут работать со знаками доллара — можно было выбрать и другие знаки.
Пример: зададим по шаблону предложение «Я $love$ $you$». Чтобы получилась осмысленная фраза, мы должны задать значения для обоих шаблонных слов. Сделаем это так:
- пусть $love$ принимает значения «тебя», «вас», «нас», «их», «всех»;
- пусть $you$ принимает значения «люблю», «ненавижу», «не люблю», «рад видеть».
Если мы попросим алгоритм случайно заменить шаблонные слова на конкретные значения из списка, то можем получить такие предложения:
- Я их люблю.
- Я тебя ненавижу.
- Я вас не люблю.
- Я всех люблю.
- Я нас рад видеть.
- …
Смотрите, большинство предложений звучит нормально, а последнее — не очень. Это не значит, что алгоритм плохой, просто мы, когда подбирали слова, не слишком подумали над их сочетанием. Поэтому основная проблема таких алгоритмов — ручная проверка и согласование всех слов и конструкций, которые используются для подстановки. Чем больше времени этому посвятить, тем более осмысленные тексты получатся у алгоритма.
Чтобы тексты получались разнообразными, можно сделать так, чтобы шаблон выбирался в зависимости от какой-то переменной. Именно так мы сделаем выбор интонации письма, официальную или неформальную.
Начало
Вот код, с которого начинается наш скрипт. В нём мы задаём шаблоны начала, тела и концовки письма, причём каждый шаблон — в двух вариантах. Первый отвечает за формальный стиль ответа, а второй — за неформальный. Всё это оформляется как массивы. Если вы не знаете, как с ними работать — решите задачу про сейф.
Интересный момент — внутри массивов a_text и b_text мы использовали шаблонное слово $somebody$. Алгоритм сначала подставит предложение вместе с этим шаблонным словом, а вторым проходом — заменит его на конкретное значение.
Все наши массивы мы объединили в один объект с помощью фигурных скобок, чтобы дальше с ним работать как с единым целым. Это позволит алгоритму найти всё в одном месте и последовательно заменить все шаблонные слова на нужные значения.
// шаблон начала письма
var intro = ['$a_intro$','$b_intro$ '];
// шаблон тела письма
var text = ['$a_text$','$b_text$'];
// шаблон концовки письма
var outro = ['$a_outro$','$b_outro$'];
// задаём набор фраз и слов для всех шаблонов сразу
var text_obj={
//структуру письма пока оставляем пустой
structure:[
''
],
// текст для начала
a_intro:['Здравствуйте.','Добрый день!'],
b_intro:['Привет!','Хэллоу!','Бонжур!'],
// варианты основного текста
a_text:['Перед вами — первое письмо в рассылке. Наш $somebody$ рад тому, что вы не прошли мимо подписки, и приглашает вас на нашу выставку, адрес — во вложении.','Меня зовут Михаил Максимов, и я — $somebody$ в этой компании. От лица всех сотрудников я рад приветствовать вас в рядах наших единомышленников.'],
b_text:['Если ты видишь это письмо, знай — наш $somebody$ здорово постарался для этого. Зато ты теперь сможешь прийти к нам на выставку и убедиться своими глазами в том, о чём мы тебе говорили!','Ты сделал это, а значит, твой $somebody$ будет в восторге! Если нет — дай нам знать, и мы это исправим.'],
// должность
somebody:['директор','руководитель','начальник отдела'],
// текст для концовки
a_outro:['Спасибо, что подписались на нашу рассылку!','Если будут вопросы — пишите.','Если письмо попало к вам по ошибке — проигнорируйте его.'],
b_outro:['Теперь ты один из нас!','Здорово, что мы теперь — команда!','Рады, что ты с нами! Пиши, если есть что спросить.'],
};
Теперь можно собрать письмо в одно целое. Для этого берём начало, середину и концовку, добавляем HTML-теги и соединяем всё в одну строку. Не стоит опасаться того, что всё будет выглядеть некрасиво — для этого мы как раз и добавляем теги, чтобы они разметили наш текст правильно и красиво.
Мы говорили, что сделаем так, чтобы алгоритм сам выбирал, в каком стиле сделать письмо. За это отвечает переменная mood: если она равна 0, то стиль будет официальным, а если она равна 1 — неформальным. Чтобы каждый раз это число определялось случайным образом, добавим функцию, которая возвращает случайное число в заданном диапазоне.
// генератор случайных чисел в диапазоне от минимального до максимального
function randz(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/ собираем письмо в одно целое
function generate_structure(){
// случайным образом определяем тон письма — официальный (0) или неформальный (1)
var mood = randz(0,1);
// результат помещаем в переменную вместе с тегами
result = '<h2>' + intro[mood] + '</h1>\n';
result += '<p>' + text[mood] + '</p>\n';
result += '<p>' + outro[mood] + '</p>\n';
// возвращаем результат — одну строку с HTML-разметкой
return result;
}
Чтобы было понятно, что будет происходить дальше в коде, заглянем немного вперёд. Алгоритм будет действовать так: возьмёт структуру письма и раз за разом будет находить все шаблонные слова со знаком доллара, находить нужный массив, откуда они берутся, и подставлять их. Так алгоритм будет работать до тех пор, пока не закончатся все шаблонные слова. Вот как, например, может выглядеть наше письмо с тегами на каждом проходе алгоритма.
1-й проход:
<h2>$a_intro$</h2>
<p>$a_text$</p>
<p>$a_outro$</p>
2-й проход:
<h2>Здравствуйте.</h2>
<p>Перед вами — первое письмо в рассылке. Наш $somebody$ рад тому, что вы не прошли мимо подписки, и приглашает вас на нашу выставку, адрес — во вложении.</p>
<p>Спасибо, что подписались на нашу рассылку!</p>
3-й проход:
<h2>Здравствуйте.</h2>
<p>Перед вами — первое письмо в рассылке. Наш директор рад тому, что вы не прошли мимо подписки, и приглашает вас на нашу выставку, адрес — во вложении.</p>
<p>Спасибо, что подписались на нашу рассылку!</p>
Как видим, алгоритм сначала заменил все шаблонные слова первого уровня, а затем — второго уровня. Можно делать сколько угодно вложений — алгоритм заменит их все на нормальные слова.
Меняем шаблонные слова на текст
Здесь принцип работы такой:
находим все слова, где есть значки доллара;
убираем их и получаем название массива в объекте, где хранятся значения для этого шаблонного слова;
идём в этот массив и случайно выбираем оттуда значение;
подставляем это значение вместо шаблонного слова;
делаем так до тех пор, пока не закончатся все шаблонные слова.
// убираем знаки доллара после замены шаблонного слова на реальный текст
function parse_keywords(string){
// задаём шаблон поиска таких слов
pattern = /\$\w+\$/g;
// проверяем, есть ли в строке нужные нам слова
keyword = string.match(pattern);
// если есть
if(keyword){
// пока не закончатся все такие слова в строке…
for (var i = keyword.length - 1; i >= 0; i--) {
// убираем значок доллара
keyword[i]=keyword[i].replace(/\$/g,'');
}
}
// возвращаем слово без знаков доллара
return keyword;
}
// выбираем случайный элемент массива
function randomize (arr){
return arr[Math.floor(Math.random()*arr.length)];
}
// меняем одно слово на другое
function replace_keyword(source,keyword,variant){
return(source.replace('$'+keyword+'$',variant));
}
//подставляем случайным образом готовые слова вместо шаблонных слов со знаком доллара
function bake(object){
// переменная, в которой на выходе получится готовый текст
var result=randomize(object['structure']);
// пока есть шаблонные слова со знаком доллара
do{
// выбираем их
keywords=parse_keywords(result);
// если они точно есть, то
if(keywords){
// пока они не закончатся
for (var i = keywords.length - 1; i >= 0; i--) {
// случайным образом подставляем вместо шаблонных слов с долларом слова из наборов
if(object.hasOwnProperty(keywords[i])){
result = replace_keyword(result,keywords[i],randomize(object[keywords[i]]));
}
}
}
} while(keywords);
// возвращаем готовый результат
return result;
}
Привязываем скрипт к кнопке и выводим результат
Мы уже прописали в HTML-документе, что по клику будет вызываться функция get_text(). Давайте это запрограммируем так, чтобы текст письма сразу после сборки появлялся на экране. Чтобы это сделать, используем jQuery, найдём блок для вывода текста text_here и отправим наш текст с HTML-тегами в этот блок.
Браузер сам распознает все теги и отформатирует письмо так, как нужно нам. Если вы хотите другое форматирование — поменяйте теги или пропишите в них нужные стили.
// подставляем текст с тегами в нужне место на странице
function send(text){
document.getElementById('text_here').innerHTML=text;
}
// что мы делаем по нажатию на кнопку
function get_text(){
// заводим переменную для структуры текста
var currentObject = text_obj;
// наполняем структуру текстом с шаблонными словами
currentObject.structure[0] = generate_structure();
// меняем шаблонные слова на нормальный текст
result=bake(currentObject);
// выводим результат
send(result);
}
Готовый код скрипта
// шаблон начала письма
var intro = ['$a_intro$','$b_intro$ '];
// шаблон тела письма
var text = ['$a_text$','$b_text$'];
// шаблон концовки письма
var outro = ['$a_outro$','$b_outro$'];
// задаём набор фраз и слов для всех шаблонов сразу
var text_obj={
//структуру письма пока оставляем пустой
structure:[
''
],
// текст для начала
a_intro:['Здравствуйте.','Добрый день!'],
b_intro:['Привет!','Хэллоу!','Бонжур!'],
// варианты основного текста
a_text:['Перед вами — первое письмо в рассылке. Наш $somebody$ рад тому, что вы не прошли мимо подписки, и приглашает вас на нашу выставку, адрес — во вложении.','Меня зовут Михаил Максимов, и я — $somebody$ в этой компании. От лица всех сотрудников я рад приветствовать вас в рядах наших единомышленников.'],
b_text:['Если ты видишь это письмо, знай — наш $somebody$ здорово постарался для этого. Зато ты теперь сможешь прийти к нам на выставку и убедиться своими глазами в том, о чём мы тебе говорили!','Ты сделал это, а значит, твой $somebody$ будет в восторге! Если нет — дай нам знать, и мы это исправим.'],
// должность
somebody:['директор','руководитель','начальник отдела'],
// текст для концовки
a_outro:['Спасибо, что подписались на нашу рассылку!','Если будут вопросы — пишите.','Если письмо попало к вам по ошибке — проигнорируйте его.'],
b_outro:['Теперь ты один из нас!','Здорово, что мы теперь — команда!','Рады, что ты с нами! Пиши, если есть что спросить.'],
} ;
// убираем знаки доллара после замены шаблонного слова на реальный текст
function parse_keywords(string){
// задаём шаблон поиска таких слов
pattern = /\$\w+\$/g;
// проверяем, есть ли в строке нужные нам слова
keyword = string.match(pattern);
// если есть
if(keyword){
// пока не закончатся все такие слова в строке…
for (var i = keyword.length - 1; i >= 0; i--) {
// убираем значок доллара
keyword[i]=keyword[i].replace(/\$/g,'');
}
}
// возвращаем слово без знаков доллара
return keyword;
}
// генератор случайных чисел в диапазоне от минимального до максимального
function randz(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// выбираем случайный элемент массива
function randomize (arr){
return arr[Math.floor(Math.random()*arr.length)];
}
// меняем одно слово на другое
function replace_keyword(source,keyword,variant){
return(source.replace('$'+keyword+'$',variant));
}
//подставляем случайным образом готовые слова вместо шаблонных слов со знаком доллара
function bake(object){
// переменная, в которой на выходе получится готовый текст
var result=randomize(object['structure']);
// пока есть шаблонные слова со знаком доллара
do{
// выбираем их
keywords=parse_keywords(result);
// если они точно есть, то
if(keywords){
// пока они не закончатся
for (var i = keywords.length - 1; i >= 0; i--) {
// случайным образом подставляем вместо шаблонных слов с долларом слова из наборов
if(object.hasOwnProperty(keywords[i])){
result = replace_keyword(result,keywords[i],randomize(object[keywords[i]]));
}
}
}
} while(keywords);
// возвращаем готовый результат
return result;
}
// собираем письмо в одно целое
function generate_structure(){
// случайным образом определяем тон письма — официальный (0) или неформальный (1)
var mood = randz(0,1);
// результат помещаем в переменную вместе с тегами
result = '<h2>' + intro[mood] + '</h1>\n';
result += '<p>' + text[mood] + '</p>\n';
result += '<p>' + outro[mood] + '</p>\n';
// возвращаем результат — одну строку с HTML-разметкой
return result;
}
// подставляем текст с тегами в нужне место на странице
function send(text){
document.getElementById('text_here').innerHTML=text;
}
// что мы делаем по нажатию на кнопку
function get_text(){
// заводим переменную для структуры текста
var currentObject = text_obj;
// наполняем структуру текстом с шаблонными словами
currentObject.structure[0] = generate_structure();
// меняем шаблонные слова на нормальный текст
result=bake(currentObject);
// выводим результат
send(result);
}
Что дальше
На самом деле возможности этого скрипта почти безграничны: при должной усидчивости и знании русского языка можно заставить этот скрипт выдавать осмысленный текст любой длины.
Например, можно генерировать ответы службы поддержки, сообщения в чат-ботах, подводки для статей или создавать резюме. Этот скрипт — убийца копирайтеров, потому что он делает за них всю основную работу. Вам остаётся только пройти по тексту и убрать шероховатости.
Кстати, в одной редакции (не в нашей) такой алгоритм несколько месяцев генерировал статьи, и никто ничего не заметил. Читателям нравилось. Совпадение?
