Tạo một chatbot cho các phòng chat Stack Exchange


39

Các thách thức

Mục tiêu của thử thách này là tạo ra một chatbot có thể chạy trong các phòng chat của Stack Exchange. Bot của bạn cần có khả năng phát hiện khi các lệnh cụ thể được đăng bởi người dùng và phản hồi nó. Đây là danh sách các lệnh và bot của bạn nên làm gì:

  • !!newest: xuất tiêu đề (không có liên kết, nhưng tiêu đề) của câu hỏi mới nhất được đăng trên trang web này (codegolf.SE).
  • !!metanewest: xuất tiêu đề của câu hỏi mới nhất được đăng trên trang web meta (meta.codegolf.SE).
  • !!questioncount: xuất số lượng câu hỏi hiện tại.
  • !!metaquestioncount: xuất số lượng câu hỏi hiện tại trên trang web meta.
  • !!tag tagname: xuất đoạn trích thẻ (mô tả ngắn) của thẻ được đưa ra làm tham số đầu tiên.
  • !!metatag tagname: giống như trên, nhưng đối với trang meta.
  • !!featured: xuất số lượng câu hỏi hiện có tiền thưởng.
  • !!metafeatured: xuất số lượng câu hỏi có thẻ [đặc trưng] trên Meta.

Quy tắc

  1. Bạn nên viết một chương trình hoàn chỉnh, không phải là một đoạn hoặc chức năng.
  2. Trong trường hợp cần thiết, bạn có thể yêu cầu tên người dùng và mật khẩu làm đầu vào (nhắc nhở về đầu vào, STDIN, đối số dòng lệnh). Điều này sẽ cần thiết nếu bạn sử dụng, ví dụ như Python hoặc Ruby, nhưng sẽ không cần thiết nếu bạn sử dụng JavaScript và chạy tập lệnh trên chính trang phòng trò chuyện.
  3. Bạn được phép sử dụng các thư viện bên ngoài để làm những thứ như WebSockets. Các thư viện này không phải tính cho số lượng nhân vật của bạn.
  4. Bạn có thể sử dụng trình bao bọc trò chuyện bên ngoài (nhưng bạn không cần phải, việc viết riêng của bạn được khuyến khích), và sau đó phải tính cho số lượng ký tự. Bạn cũng không được phép thay đổi mã của trình bao bọc. Nếu bạn sử dụng nó, bạn sử dụng nó mà không cần sửa đổi và tất cả các ký tự phải được tính (đó là một hình phạt cho việc không viết trình bao bọc của riêng bạn).

    Chỉ có mã của trình bao bọc chính nó phải được tính. Nếu có các tệp khác như ví dụ, chúng không phải tính.

  5. Không sử dụng các công cụ rút ngắn URL hoặc các cách khác có thể làm cho URL ngắn hơn: thách thức là đánh gôn một chatbot, không phải đánh gôn URL.
  6. Không có yêu cầu web, ngoại trừ những yêu cầu cần thiết để trò chuyện và nhận thông tin cần thiết để đáp ứng các lệnh.
  7. Không được phép sử dụng "sơ hở" tiêu chuẩn .
  8. Nếu ai đó gửi lệnh, bạn cần trả lời bằng tin nhắn trò chuyện có định dạng này : @user response. Vì vậy, nếu tôi viết lệnh !!featuredvà có 5 câu hỏi nổi bật, bot của bạn nên đăng @ProgramFOX 5.
  9. Nếu tôi kiểm tra bot của bạn, tôi sẽ chạy nó từ tài khoản chatbot của mình và tôi sẽ chạy nó trong phòng chat này . Tôi sẽ luôn kiểm tra các bot trong phòng đó, vì vậy không cần thiết phải cung cấp ID phòng làm đầu vào, nó sẽ luôn là 14697. ID này sẽ không được cung cấp làm đầu vào, nên được mã hóa cứng.
  10. Nếu lệnh không được tìm thấy, đầu ra @user The command [command] does not exist. Thay thế [command]bằng tên của lệnh không tồn tại. Nếu các đối số được cung cấp cho lệnh, không xuất ra các đối số, chỉ tên lệnh.
  11. Nếu một lệnh có nhiều đối số, bỏ qua các đối số không cần thiết.
  12. Nếu một lệnh không đủ đối số, đầu ra @user You have not provided enough arguments
  13. Hệ thống ngăn chặn các tin nhắn trùng lặp được đăng trong một khoảng thời gian ngắn. Vì vậy, khi kiểm tra bot của bạn, tôi sẽ không bao giờ chạy hai lệnh cho cùng một đầu ra liên tiếp (điều đó có nghĩa là bạn không phải thực hiện một hệ thống làm cho các thông báo khác nhau nếu chúng trùng lặp, bằng cách thêm một dấu chấm chẳng hạn).
  14. Hệ thống ngăn chặn quá nhiều tin nhắn được đăng trong một khoảng thời gian ngắn, vì vậy khi kiểm tra, tôi sẽ không bao giờ gửi quá nhiều lệnh trong một khoảng thời gian ngắn, điều đó có nghĩa là bot của bạn không phải lo việc này (bằng cách chờ một thời gian trước khi đăng, ví dụ).
  15. Đây là , chương trình có số lượng byte thắng ít nhất.

Bắt đầu

Đây là một số thông tin để bắt đầu với việc viết bot của bạn. Bạn không cần phải sử dụng nó, nhưng nó có thể là một hướng dẫn.

  • Để đăng nhập, trước tiên hãy đăng nhập vào nhà cung cấp OpenID. Đây sẽ luôn là Stack Exchange OpenID ( https://openid.stackexchange.com). Biểu mẫu đăng nhập được đặt tại https://openid.stackexchange.com/account/login, và cung cấp tên người dùng và mật khẩu ở đó.
  • Sau đó, đăng nhập vào stackexchange.com. Các hình thức đăng nhập được đặt tại https://stackexchange.com/users/login. Chọn Stack Exchange là nhà cung cấp OpenID.
  • Sau khi làm điều đó, đăng nhập để trò chuyện. Các hình thức đăng nhập cho đó được đặt tại http://stackexchange.com/users/chat-login. Chọn Stack Exchange là nhà cung cấp OpenID.
  • Sau đó, bạn cần phải có được của bạn fkey. Đối với điều đó, đi đến http://chat.stackexchange.com/chats/join/favoritevà lấy fkeytừ một trường đầu vào ẩn.
  • Để gửi tin nhắn, gửi yêu cầu đến http://chat.stackexchange.com/chats/14697/messages/newvà cung cấp hai tham số POST: texttham số chứa văn bản thông báo và fkeytham số chứa fkey.
  • Để xem khi nào một tin nhắn mới được đăng, bạn có thể sử dụng WebSockets (nhưng không phải, cứ thoải mái sử dụng thứ khác nếu nó ngắn hơn). Vui lòng xem câu trả lời Meta Stack Exchange này :

    Trò chuyện

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

    Băm có thể được tìm nạp bằng cách POST id phòng và fkey để http://chat.stackexchange.com/ws-auth

    Thời gian biểu là chìa khóa thời gian của json được trả về /chats/<roomno>/events.

    ID sự kiện khi một tin nhắn được đăng là 1.

  • Thật hữu ích khi xem xét các trình bao bọc trò chuyện hiện có, chẳng hạn như StackExchange-Chatty và Manishearth's ChatExchange , để xem chính xác cách thức hoạt động của nó.

3
Khoảnh khắc tôi nhìn thấy tiêu đề, tôi lập tức nghĩ "ah, ProgramFOX".
xem

Tôi đã hy vọng metafeaturedsẽ có nghĩa là câu hỏi tiền thưởng trên meta, nhưng ... cảm ơn :-)
John Dvorak

@JanDvorak Metas trên mỗi trang web không có tiền thưởng, vì vậy tôi không thể sử dụng nó. Khi tôi viết thử thách này, tôi đã quên rằng Meta có thẻ [đặc trưng], vì vậy cảm ơn lời đề nghị của bạn!
Chương trìnhFOX

Những gì tôi đã làm để xem liệu một tin nhắn mới đã được đăng hay chưa, hãy kiểm tra cứ sau 2 giây thông qua JS nếu tin nhắn cuối cùng không phải là tôi (mục cuối cùng trong lớp)
Cilan

Chúng tôi đã có một cái ở đây
Ông Alien

Câu trả lời:


14

JavaScript + jQuery, 1362 1258 byte

Chơi gôn bằng công cụ khai thác:

$(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})})

Bạn phải chạy tập lệnh trực tiếp trong trình duyệt (sử dụng các tác phẩm jQuery của Stack Exchange):

  1. Mở http://chat.stackexchange.com/rooms/14697/chatbot-challenge-on-programming-puheads-code-golf
  2. Dán đoạn mã trên vào bàn điều khiển
  3. Nhập một số lệnh trong trò chuyện

Nó có thể được đánh gôn nhiều hơn, nhưng không thể bị làm phiền.


Chưa chơi gôn:

$(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});
});

Rất vui, cảm ơn vì đã đăng câu trả lời tại đây! Bây giờ tôi sẽ chạy thử nghiệm trong phòng. Dù sao, tôi nghĩ rằng bạn có thể lưu một số ký tự bằng cách sử dụng nhiều biến một chữ cái hơn và tránh updatelatestMessage.
Chương trìnhFOX

Tuyệt quá! Bạn đã vượt qua tất cả các bài kiểm tra . Điều kỳ lạ duy nhất tôi nhận thấy là bot của bạn trả về số lượng câu hỏi khác so với trên trang chủ, nhưng tôi thấy rằng API đã trả về số đó, vì vậy tôi đã báo cáo điều này trên Meta và đánh dấu trường hợp kiểm tra là chính xác. Làm tốt! +1
Chương trìnhFOX

Tôi thấy bạn làm bot ngắn hơn. Tốt đẹp! :) Tôi đã kiểm tra lại và bạn vẫn vượt qua tất cả các bài kiểm tra.
Chương trìnhFOX

Khá muộn, nhưng tôi chỉ thấy sự cải thiện một char: bạn có thể thay thế 0==e.items.lengthvới 1>e.items.lengthvì chiều dài sẽ không bao giờ đi dưới số không.
Chương trìnhFOX
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.