Создать чат-бота для чатов Stack Exchange


39

Соревнование

Цель этой задачи - создать чат-бота, который может работать в чатах Stack Exchange. Ваш бот должен уметь определять, когда пользователь отправляет определенные команды, и отвечать на них. Это список команд и то, что должен делать ваш бот:

  • !!newest: вывести заголовок (без ссылки, но заголовок) нового вопроса, размещенного на этом сайте (codegolf.SE).
  • !!metanewest: вывести заголовок нового вопроса, опубликованного на мета-сайте (meta.codegolf.SE).
  • !!questioncount: вывести текущий счетчик вопросов.
  • !!metaquestioncount: вывести текущий счетчик вопросов на мета-сайте.
  • !!tag tagname: вывести отрывок (краткое описание) тега, который указан в качестве первого параметра.
  • !!metatag tagname: то же, что и выше, но для мета сайта.
  • !!featured: вывести количество вопросов, которые в настоящее время имеют награду.
  • !!metafeatured: вывести количество вопросов с тегом [featured] в Meta.

правила

  1. Вы должны написать полную программу, а не фрагмент или функцию.
  2. Если это необходимо, вы можете запросить имя пользователя и пароль в качестве входных данных (запрос ввода, STDIN, аргументы командной строки). Это будет необходимо, если вы используете, например, Python или Ruby, но в этом нет необходимости, если вы используете JavaScript и запускаете скрипт на самой странице чата.
  3. Вы можете использовать внешние библиотеки для таких вещей, как WebSockets. Эти библиотеки не должны рассчитывать на количество ваших символов.
  4. Вы можете использовать внешнюю оболочку чата (но вам это не нужно, рекомендуется писать свои собственные), а затем это должно учитываться для подсчета символов. Вам также не разрешено изменять код оболочки. Если вы используете его, вы используете его без изменений, и все символы должны быть подсчитаны (это штраф за то, что вы не написали свою собственную обертку).

    Только код самой оболочки должен рассчитывать. Если есть другие файлы, такие как примеры, их не нужно считать.

  5. Не используйте сокращатели URL-адресов или другие способы, которые могут сделать URL-адреса короче: задача состоит в том, чтобы поиграть в чат-робота, а не в URL-адресе.
  6. Нет веб-запросов, кроме тех, которые необходимы для чата и получения информации, необходимой для ответа на команды.
  7. Использование Стандартных «лазеек» не допускается.
  8. Если кто - то отправляет команду, вы должны реагировать с сообщением чата этого формата: @user response. Итак, если я напишу команду !!featuredи у вас будет 5 вопросов, ваш бот должен написать @ProgramFOX 5.
  9. Если я протестирую вашего бота, я запустлю его из своей учетной записи в чате и запустю в этом чате . Я всегда буду проверять ботов в этой комнате, поэтому нет необходимости указывать идентификатор комнаты в качестве входных данных, это всегда будет 14697. Этот идентификатор не будет вводиться в качестве входных данных, он должен быть жестко закодирован.
  10. Если команда не найдена, выведите @user The command [command] does not exist. Заменить [command]на имя несуществующей команды. Если аргументы предоставлены команде, не выводите аргументы, только имя команды.
  11. Если команда имеет много аргументов, игнорируйте аргументы, которые не нужны.
  12. Если у команды недостаточно аргументов, выведите @user You have not provided enough arguments
  13. Система предотвращает размещение дублированных сообщений в короткие сроки. Таким образом, при тестировании вашего бота я никогда не буду запускать две команды, которые выдают один и тот же результат подряд (что означает, что вам не нужно реализовывать систему, которая отличает сообщения, если они являются дубликатами, например, путем добавления точки).
  14. Система предотвращает отправку слишком большого количества сообщений за короткий промежуток времени, поэтому при тестировании я никогда не отправлю слишком много команд за короткий промежуток времени, а это значит, что вашему боту не нужно заботиться об этом (ожидая некоторое время перед публикацией, например).
  15. Это , выигрывает программа с наименьшим количеством байтов.

Начиная

Вот некоторая информация, чтобы начать писать свой бот. Вы не должны использовать это, но это может быть руководством.

  • Чтобы войти, сначала войдите в систему OpenID провайдера. Это всегда будет Stack Exchange OpenID ( https://openid.stackexchange.com). Форма входа находится по адресу https://openid.stackexchange.com/account/loginи укажите там имя пользователя и пароль.
  • Затем войдите в систему stackexchange.com. Форма входа находится по адресу https://stackexchange.com/users/login. Выберите Stack Exchange в качестве поставщика OpenID.
  • После этого войдите в чат. Форма входа для этого находится по адресу http://stackexchange.com/users/chat-login. Выберите Stack Exchange в качестве поставщика OpenID.
  • Тогда вам нужно получить свой fkey. Для этого перейдите http://chat.stackexchange.com/chats/join/favoriteи получите fkeyиз скрытого поля ввода.
  • Чтобы опубликовать сообщение, отправьте запрос http://chat.stackexchange.com/chats/14697/messages/newи предоставьте два параметра POST: textпараметр, содержащий текст сообщения, и fkeyпараметр, содержащийfkey .
  • Чтобы увидеть, когда новое сообщение публикуется, вы можете использовать WebSockets (но не обязательно, не стесняйтесь использовать что-то еще, если оно короче). Пожалуйста, посмотрите ответ Meta Stack Exchange :

    Чат

    (wss://chat.sockets.stackexchange.com/events/<roomnumber>/<somehash>?l=<timethingy>)

    Хеш можно получить, поместив идентификатор комнаты и fkey в http://chat.stackexchange.com/ws-auth

    Timethingy - это ключ времени, возвращаемый JSON /chats/<roomno>/events.

    Идентификатор события, когда сообщение отправлено 1.

  • Полезно взглянуть на существующие обертки чата, такие как StackExchange-Chatty от Doorknob и ChatExchange от Manishearth , чтобы увидеть, как именно это работает.

3
В тот момент, когда я увидел название, я сразу подумал: «А, ProgramFOX».
seequ

Я надеялся, metafeaturedчто это будет означать обильные вопросы по мета, но ... спасибо :-)
Джон Дворак

@JanDvorak Метас на сайте не имеет щедрости, поэтому я не могу это использовать. Когда я писал этот тест, я забыл, что у Meta есть тег [featured], так что спасибо за ваше предложение!
ProgramFOX

То, что я сделал, чтобы увидеть, если новое сообщение было отправлено, было проверять каждые 2 секунды с помощью JS, если последнее сообщение не было мной (последний элемент в классе)
Cilan

У нас уже есть один здесь
г-н Чужой

Ответы:


14

JavaScript + jQuery, 1362 1258 байт

Игра в гольф с помощью миниатора:

$(function(){function e(){function e(e,t){$("#input").val("@"+$(e).parents(".user-container").find(".username").eq(0).text()+" "+t),$("#sayit-button").click()}var i,a=$(t),s=a.map(function(e,t){return t.id}),r=s.slice(-1)[0]
n!=r&&(i=a.slice($.inArray(n,s)+1),n=r,i.map(function(t,n){var i,a,s,r,o,u,c,f=n.textContent.match(/!!(\S+)(?:\s+(\S+))?/)
if(f){switch(i=f[1],a=f[2],s="codegolf",0==i.indexOf("meta")&&(s="meta."+s,i=i.slice(4)),r="?site="+s,c=0,i){case"newest":o=["questions","&order=desc&sort=creation"],u=function(e){return e.items[0].title}
break
case"questioncount":o=["info",""],u=function(e){return e.items[0].total_questions}
break
case"tag":if(!a){c=1
break}o=["tags/"+a+"/wikis",""],u=function(e){return 0==e.items.length?"Tag not found":e.items[0].excerpt}
break
case"featured":o=0==s.indexOf("meta.")?["questions","&tagged=featured"]:["questions/featured",""],u=function(e){var t=e.items.length
return(e.items.has_more?"more than ":"")+t}}c?e(n,"You have not provided enough arguments"):o?$.get("http://api.stackexchange.com/2.2/"+o[0]+r+o[1],function(t){e(n,u(t))}):e(n,"The command "+i+" does not exist")}}))}var t="[id^=message-]",n=$(t).eq(-1).attr("id")
new MutationObserver(e).observe($("#chat").get(0),{childList:!0,subtree:!0})})

Вы должны запустить скрипт прямо в браузере (используя jQuery из Stack Exchange):

  1. Откройте http://chat.stackexchange.com/rooms/14697/chatbot-challenge-on-programming-puzzles-code-golf
  2. Вставьте приведенный выше код в консоль
  3. Введите несколько команд в чате

Это может быть гораздо больше, но не может быть обеспокоен.


Un-golfed:

$(function() {
    var sel = '[id^=message-]';
    var latestMessage = $(sel).eq(-1).attr('id');
    function update() {
        var messages = $(sel);
        var ids = messages.map(function(i, x) { return x.id; });
        var newest = ids.slice(-1)[0];
        if(latestMessage == newest) {
            return;
        }
        var newMessages = messages.slice($.inArray(latestMessage, ids) + 1);
        latestMessage = newest;
        newMessages.map(function(i, x) {
            var m = x.textContent.match(/!!(\S+)(?:\s+(\S+))?/);
            if(!m) {
                return;
            }
            var c = m[1];
            var a = m[2];
            var s = 'codegolf';
            if(c.indexOf('meta') == 0) {
                s = 'meta.' + s;
                c = c.slice(4);
            }
            var site = '?site=' + s;
            var url;
            var extractor;
            var too_few_args = 0;
            switch(c) {
                case 'newest':
                    var url = ['questions', '&order=desc&sort=creation'];
                    extractor = function(data) {
                        return data.items[0].title;
                    };
                    break;
                case 'questioncount':
                    url = ['info', ''];
                    extractor = function(data) {
                        return data.items[0].total_questions;
                    };
                    break;
                case 'tag':
                    if(!a) {
                        too_few_args = 1;
                        break;
                    }
                    url = ['tags/' + a + '/wikis', ''];
                    extractor = function(data) {
                        if(data.items.length == 0) {
                            return 'Tag not found';
                        }
                        return data.items[0].excerpt;
                    };
                    break;
                case 'featured':
                    url = s.indexOf('meta.') == 0? ['questions', '&tagged=featured']: ['questions/featured', ''];
                    extractor = function(data) {
                        var l = data.items.length;
                        return (data.items.has_more? 'more than ': '') + l;
                    }
                    break;
            }
            if(too_few_args) {
                write(x, 'You have not provided enough arguments');
            } else if(!url) {
                write(x, 'The command ' + c + ' does not exist');
            } else {
                $.get('http://api.stackexchange.com/2.2/' + url[0] + site + url[1], function(data) {
                    write(x, extractor(data));
                });
            }
        });

        function write(x, m) {
            $('#input').val('@' + $(x).parents('.user-container').find('.username').eq(0).text() + ' ' + m);
            $('#sayit-button').click();
        }
    }
    new MutationObserver(update).observe($('#chat').get(0), {childList: true, subtree: true});
});

Хорошо, спасибо за размещение ответа здесь! Теперь я собираюсь запустить тесты в комнате. В любом случае, я думаю, что вы можете сохранить некоторые символы, используя больше однобуквенных переменных, избегая updateи latestMessage.
ProgramFOX

Большой! Вы прошли все тесты . Единственное, что я заметил, было то, что ваш бот возвратил счетчик вопросов, отличный от того, что был на домашней странице, но я увидел, что API вернул это число, поэтому я сообщил об этом в Meta и отметил тестовый пример как правильный. Отлично сработано! +1
ProgramFOX

Я видел, что ты сделал бот короче. Ницца! :) Я перепроверил это, и вы все еще проходите все испытания.
ProgramFOX

Скорее поздно, но я только что нашел улучшение на один-обугленное: вы можете заменить 0==e.items.lengthс 1>e.items.lengthтем , что длина никогда не опускается ниже нуля.
ProgramFOX
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.