Utwórz chatbota dla czatów Stack Exchange


39

Wyzwanie

Celem tego wyzwania jest stworzenie chatbota, który będzie działał na czatach Stack Exchange. Twój bot musi być w stanie wykryć, kiedy użytkownik opublikuje określone polecenia i na nie odpowiedzieć. Oto lista poleceń i to, co powinien zrobić twój bot:

  • !!newest: wypisuje tytuł (bez linku, ale tytuł) najnowszego pytania opublikowanego na tej stronie (codegolf.SE).
  • !!metanewest: wypisuje tytuł najnowszego pytania opublikowanego na stronie meta (meta.codegolf.SE).
  • !!questioncount: wyświetla bieżącą liczbę pytań.
  • !!metaquestioncount: wyświetla bieżącą liczbę pytań w witrynie meta.
  • !!tag tagname: wypisuje fragment znacznika (krótki opis) znacznika, który jest podany jako pierwszy parametr.
  • !!metatag tagname: tak samo jak powyżej, ale dla strony z meta.
  • !!featured: wypisz liczbę pytań, które aktualnie mają nagrodę.
  • !!metafeatured: wypisz liczbę pytań, które mają tag [polecany] w Meta.

Zasady

  1. Powinieneś napisać pełny program, a nie fragment kodu lub funkcję.
  2. W razie potrzeby możesz podać nazwę użytkownika i hasło jako dane wejściowe (monit o podanie, STDIN, argumenty wiersza poleceń). Będzie to konieczne, jeśli używasz na przykład Python lub Ruby, ale nie będzie konieczne, jeśli używasz JavaScript i uruchamiasz skrypt na samej stronie pokoju czatu.
  3. Możesz używać bibliotek zewnętrznych do robienia rzeczy takich jak WebSockets. Te biblioteki nie muszą liczyć się do liczby twoich postaci.
  4. Państwo może użyć zewnętrznego czat opakowanie (ale nie muszą, pisząc własną zachęca), a następnie, że musi liczyć na liczby znaków. Nie możesz również zmieniać kodu opakowania. Jeśli go używasz, używasz go bez modyfikacji, a wszystkie postacie muszą być policzone (jest to kara za nie napisanie własnego opakowania).

    Tylko kod samego opakowania musi się liczyć. Jeśli istnieją inne pliki, takie jak przykłady, nie muszą się one liczyć.

  5. Bez użycia skracaczy URL lub innych sposobów, które mogą skrócić adresy URL: wyzwaniem jest gra w chatbota w golfa, a nie URL.
  6. Brak żądań internetowych, z wyjątkiem tych niezbędnych do czatu i uzyskania informacji niezbędnych do odpowiedzi na polecenia.
  7. Korzystanie ze standardowych „luk” jest niedozwolone.
  8. Jeśli ktoś stanowisk polecenia, trzeba odpowiedzieć za pomocą wiadomości czatu w tym formacie: @user response. Więc jeśli napiszę polecenie !!featuredi pojawi się 5 polecanych pytań, twój bot powinien opublikować @ProgramFOX 5.
  9. Jeśli przetestuję twojego bota, uruchomię go z mojego konta chatbota i uruchomię w tym pokoju rozmów . Zawsze będę testować boty w tym pokoju, więc nie trzeba podawać identyfikatora pokoju jako danych wejściowych, zawsze będzie to 14697. Ten identyfikator nie będzie podawany jako dane wejściowe, powinien być zakodowany na stałe.
  10. Jeśli polecenie nie zostanie znalezione, wyjdź @user The command [command] does not exist. Zastąp [command]nazwą nieistniejącego polecenia. Jeśli argumenty zostaną przekazane do polecenia, nie wysyłaj argumentów, tylko nazwa polecenia.
  11. Jeśli polecenie ma wiele argumentów, zignoruj ​​niepotrzebne argumenty.
  12. Jeśli polecenie nie ma wystarczającej liczby argumentów, wyjdź @user You have not provided enough arguments
  13. System zapobiega wysyłaniu zduplikowanych wiadomości w krótkim czasie. Dlatego podczas testowania bota nigdy nie uruchamiam dwóch poleceń, które dają te same dane wyjściowe kolejno (co oznacza, że ​​nie musisz implementować systemu, który odróżnia komunikaty, jeśli są duplikatami, na przykład poprzez dodanie kropki).
  14. System zapobiega wysyłaniu zbyt wielu wiadomości w krótkim czasie, więc podczas testowania nigdy nie wyślę zbyt wielu poleceń w krótkim czasie, co oznacza, że ​​twój bot nie musi się tym zajmować (czekając trochę czasu na przykład przed opublikowaniem).
  15. Jest to program, który wygrywa najmniej bajtów.

Pierwsze kroki

Oto kilka informacji na temat pisania bota. Nie musisz tego używać, ale może to być wskazówka.

  • Aby się zalogować, najpierw zaloguj się do dostawcy OpenID. Zawsze będzie to Stack Exchange OpenID ( https://openid.stackexchange.com). Formularz logowania znajduje się pod adresem https://openid.stackexchange.com/account/logini podaje tam nazwę użytkownika i hasło.
  • Następnie zaloguj się do stackexchange.com. Formularz logowania znajduje się pod adresem https://stackexchange.com/users/login. Wybierz Stack Exchange jako dostawcę OpenID.
  • Po wykonaniu tej czynności zaloguj się na czacie. Formularz logowania znajduje się na stronie http://stackexchange.com/users/chat-login. Wybierz Stack Exchange jako dostawcę OpenID.
  • Następnie musisz zdobyć fkey. W tym celu przejdź do ukrytego pola wejściowego http://chat.stackexchange.com/chats/join/favoritei pobierz go fkey.
  • Aby opublikować wiadomość, wyślij żądanie http://chat.stackexchange.com/chats/14697/messages/newi podaj dwa parametry POST: textparametr zawierający tekst wiadomości oraz fkeyparametr zawierający fkey.
  • Aby zobaczyć, kiedy zostanie opublikowana nowa wiadomość, możesz użyć WebSockets (ale nie musisz, możesz użyć czegoś innego, jeśli jest krótsza). Zobacz odpowiedź na Meta Stack Exchange :

    Czat

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

    Hash można pobrać, POSTując identyfikator pokoju i fkey do http://chat.stackexchange.com/ws-auth

    Harmonogram to klucz czasowy jsona zwróconego przez /chats/<roomno>/events.

    Identyfikator zdarzenia po opublikowaniu wiadomości to 1.

  • Przydatne jest spojrzenie na istniejące opakowania czatów, takie jak StackExchange-Chatty Doorknob i ChatExchange Manishearth , aby zobaczyć, jak to dokładnie działa.

3
W momencie, gdy zobaczyłem tytuł, od razu pomyślałem „ah, ProgramFOX”.
patrz

Miałem nadzieję, metafeaturedże oznaczą to szczere pytania na temat meta, ale ... dzięki :-)
John Dvorak

@JanDvorak Meta dla poszczególnych witryn nie ma zleceń, więc nie mogę ich użyć. Kiedy napisałem to wyzwanie, zapomniałem, że Meta ma [polecany] tag, więc dziękuję za twoją sugestię!
ProgramFOX

Co zrobiłem, aby zobaczyć, czy nowa wiadomość została opublikowana, sprawdzałem co 2 sekundy za pośrednictwem JS, czy ostatnia wiadomość nie była przeze mnie (ostatni element w klasie)
Cilan

Mamy już jednego tutaj
Mr. Alien

Odpowiedzi:


14

JavaScript + jQuery, 1362 1258 bajtów

Gra w golfa za pomocą minifikatora:

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

Musisz uruchomić skrypt bezpośrednio w przeglądarce (używając jQuery Stack Stack):

  1. Otwórz http://chat.stackexchange.com/rooms/14697/chatbot-challenge-on-programming-puzzles-code-golf
  2. Wklej powyższy kod w konsoli
  3. Wpisz niektóre polecenia na czacie

Można było grać w golfa o wiele więcej, ale nie przeszkadzało.


Bez golfa:

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

Fajnie, dziękuję za opublikowanie odpowiedzi tutaj! Teraz przeprowadzę testy w pokoju. W każdym razie, myślę, że możesz zapisać niektóre znaki, używając więcej literowych zmiennych i unikając updatei latestMessage.
ProgramFOX,

Świetny! Zdałeś wszystkie testy . Jedyną dziwną rzeczą, którą zauważyłem, było to, że Twój bot zwrócił inną liczbę pytań niż na stronie głównej, ale zobaczyłem, że API zwrócił ten numer, więc zgłosiłem to na Meta i oznaczyłem test jako poprawny. Dobra robota! +1
ProgramFOX,

Widziałem, że skróciłeś bota. Miły! :) Ponownie przetestowałem i nadal zdajesz wszystkie testy.
ProgramFOX,

Dość późno, ale ja po prostu znaleźć lepszy jeden char: można zastąpić 0==e.items.lengthz 1>e.items.lengthponieważ długość nigdy nie spada poniżej zera.
ProgramFOX
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.