Member Avatar for diafol

since_id

I got a long poll in PHP yesterday.

Member Avatar for diafol

Yes, I did in js too, but it seems volatile. Probably my code.

That's confused me even more. So is it last_id or since_id. A one word answer will suffice :)

You pass in since_id as a parameter and you retrieve last_id in the json results as the id that you're meant to pass in for the next request.

As long polling is meant to run in a single while loop, there is currently a limit in that you can only have one outstanding long polling request per IP address per chat room.

I am still working on figuring out better ways to ensure that long polling running in a LAMP environment doesn't take down our servers!

Member Avatar for diafol

OK, here's my js/jQ and it seems to be using long polling and the since_id also seems to be working:

//EDIT - THis doesn't work properly.

<div id="chat"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
<!--
var firstRun = true;
getChat();

function getChat(lp)
{
    var usenum = (!lp) ? 0 : lp;

    $.ajax({url: 'http://www.daniweb.com/api/forums/3/chat', data: {long_polling: 1, since_id: usenum}, dataType: 'jsonp'})
      .always(function(data){
        id = parseInt(data.polling.last_id);
        if(data.data)
        {
            chatData = data.data;
            if(firstRun === true)
            {
                initializeChat(chatData); 
            }else if(parseInt(usenum) !== parseInt(id)){
                appendChat(chatData, usenum);   
            }
            usenum = id;
        }
        firstRun = false;
        getChat(id);
      });
}

function initializeChat(data)
{
    str = '';
    $.each(data, function(i,v){
        str += writeMessage(v);
    });
    $('#chat').html(str);   
}

function appendChat(data, last_id)
{
    str = '';
    $.each(data, function(i,v){
        if(v.id > last_id)  str += writeMessage(v);
    });
    if(str !== '') $('#chat').append(str);
}

function writeMessage(record)
{
    return '<strong>' + record.id + ' <em>' + record.chatter.username + '</em>:</strong> ' + record.message + "<br />";
}

//-->
</script>

Doubtless, I'll get some jQ aficionados ripping this to bits, but it may be a starting point for others :)

commented: Nice one, thanks for sharing. +0

I just want to make mention that, in order to avoid the possibility of retrieving stale data, you will want to pass in an access token. The following is a potentially-dangerous situation:

  • You request ?since_id=4000 and it times out after 30 seconds without returning any messages
  • You reissue a a request for ?since_id=4000 in a loop, but because the previous results have been cached, no new messages will ever be found and you'll end up in a perpetual infinite loop
Member Avatar for diafol

I retract the code :(

It's bust. With the access token, it loops 2/second. No update. I've wasted enough time on it now :)
OK on to something else.

Hmm, I'm really not sure why that would happen. I'll have a look at it sometime this weekend.

Dani, when I use the API to enter a message into chat, the browser chat interface doesn't seem to automatically refresh.

It did in my test. :( Please start a new thread and post your code.

Please note I can only personally offer assistance if it's in PHP.

Member Avatar for diafol

You reissue a a request for ?since_id=4000 in a loop, but because the previous results have been cached, no new messages will ever be found and you'll end up in a perpetual infinite loop

Is cache-buster enabled?

Because I have this:

$.ajaxSetup({
    cache: false
});

Doesn't seem to make a difference. Access token or otherwise. This is really frustrating :(

//EDIT

The reason for the 2 calls/second - I THINK - is when I refresh the page and press the button again - could it be due to 'only one request from an ip address'? Could the last ajax request still be waiting when I start a new one?

Is cache-buster enabled?

That jQuery setting only works when passing in an access token. Otherwise it doesn't.

Could the last ajax request still be waiting when I start a new one?

Once you make a long-polling call to the server, then you cannot issue any additional calls to the same chat room from the same IP address for up to 30 seconds, even if you closed your web browser or terminated the script on your end. It will simply instantaneously return back an error indicating so.

Incidentally, do you think that one-call-at-a-time restriction when long polling is too strict?

Member Avatar for diafol

I don't think it's too strict, it's just I wasn't expecting the quick-light-speed loop when I was within the 30 seconds of the last call. Is there a catchable error code?

BTW - I issue an access token call and get the whole access token in the url, but there's no update on polling, even if I add some messages to chat. Here's my current code (I couldn't stop fiddling!)

/?EDIT

Did you change anything? The popup window now goes straight to 'edit your profile' page

Here's my current state of affairs:

<!DOCTYPE HTML>
<html>
<head>
</head>
<body>

<div id="chat"></div><button class="dw-update">Start</button>
<div id="clock"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
<!--
    var client_id = 24;
    var access_token;
    var clock = 0;

    //DW OAuth Code
    $(function() {
        if (window.opener != null && !window.opener.closed) {
            opener.location.hash = window.location.hash;
            window.close();
        }
        else {
            $(window).on('hashchange', function() {
                if (window.location.hash != '') {
                    var string = window.location.hash.substr(1);
                    var query = string.split('&');
                    var param;
                    for (var i = 0; i < query.length; i++) {
                        param = query[i].split('=');
                        if (param[0] == 'access_token') {
                            access_token = param[1];
                            break;
                        }
                    }
                    if (access_token !== undefined) {
                        getChat(0, true);
                    }
                }
            });

        }       
    }); 

    $('.dw-update').click(function(e){
        e.preventDefault();
        var url = 'http://www.daniweb.com/api/oauth/dialog?client_id=' + client_id + '&redirect_uri=' + window.location;
        var dialog = window.open('', 'oauth', 'height=560,width=1000');
        dialog.location = url;

    });


    function getChat(lp, firstRun)
    {
        var usenum = lp;
        var getAll = firstRun;

        $.ajax({
            url: 'http://www.daniweb.com/api/forums/3/chat',
            data: {long_polling: 1, since_id: usenum},
            //cache: false,
            dataType: 'jsonp'
        }).always(function(data){
            clock += 1;
            id = parseInt(data.polling.last_id) ;
            if(data.data)
            {
                chatData = data.data;
                if(getAll === true)
                {
                    console.log('initializeCode');
                    initializeChat(chatData); 
                }else if(parseInt(usenum) !== parseInt(id)){
                    console.log('appendCode');
                    appendChat(chatData, usenum);   
                }
                console.log(clock + ' THIS RUN since_id: ' + usenum + ' polling.last_id: ' + id + '\nNEXT RUN since_id: ' + id);

            }else{
                console.log(clock + ' THIS RUN since_id: ' + usenum + ' polling.last_id: ' + id + ' NO RECORDS\nNEXT RUN since_id: ' + id); 
            }
            getChat(id, false);
        });
    }

    function writeMessage(record)
    {
        return '<strong>' + record.id + ' <em>' + record.chatter.username + '</em>:</strong> ' + record.message + "<br />";
    }

    function initializeChat(data)
    {
        str = '';
        $.each(data, function(i,v){
            str += writeMessage(v);
        });
        $('#chat').html(str);   
    }

    function appendChat(data, last_id)
    {
        str = '';
        $.each(data, function(i,v){
            if(v.id > last_id)  str += writeMessage(v);
        });
        if(str !== '') $('#chat').append(str);
    }

//-->
</script>
</body>
</html>

Is there a catchable error code?

All API errors follow the same format:

  • If JSON request, then will output an HTTP 400 error with an html page saying the error message
  • If JSONP request, then will output a HTTP 200 with a JSON object that includes an 'error' property saying the error message

If you are making a JSONP request, then if data.error exists, there's a problem.

Did you change anything? The popup window now goes straight to 'edit your profile' page

Sorry, fixed!! Thought it would do something to help the issues you were having with chat, but I had the line in the wrong place. Nevermind. Just ignore me.

Member Avatar for diafol

OK - 'data.error' is what I was looking for :)

Diafol,

I copied and pasted your code exactly and simply replaced client_id = 1 at the beginning, and it works fine.

Member Avatar for diafol

Yes, just tried it and it works! WOOT! I'm really pleased - jS/jQ not my strong point. THanks for the background work. :)

OK, now for posting.

I just double-checked the PHP example in the APi documentation page and it works as designed.

Dani, when I use the API to enter a message into chat, the browser chat interface doesn't seem to automatically refresh.

Sorry for the delayed response. I was confused by what you were saying at first. This is by design. The DaniWeb built-in chat interface only automatically pulls messages that are not your own, since the Javascript front-end side of things handles your own messages that you type in.

Oh, that's good to hear. Thanks for the response.

Member Avatar for diafol

Is it possible to post data via js? I'm using this at the moment:

function postMsg()
{
    console.log('started post');
    $.ajax({
        url:        'http://www.daniweb.com/api/chat',
        method:     'post',
        data: {     'room_id': 120589,
                    'message': 'Hello World',
                    'room_type': 'member',
                    'access_token': access_token
              }//,
        //dataType: 'jsonp'         
    }).always(function(data){
        //console.log(JSON.stringify(data));
    });
}

Now, when I post, my own chat implementation (the one I've been building), shows the message 'Hello World' immediately (triggers the longpoll to return). However I get this message in the console:

XMLHttpRequest cannot load http://www.daniweb.com/api/chat. Origin http://cal.local is not allowed by Access-Control-Allow-Origin.

Which I sort of expected, but thought nothing of it, but strangely, my DaniWeb shoutbox isn't getting updated. Only when I refresh the shoutbox do I see the 'Hello World'. Even entering a message in the shoutbox itself doesn't prompt 'stored but invisible' messages from appearing - but there again if self-messaging is only being pushed to the Daniweb shoutbox via js, then that may explain it.

Here's a screencast for what it's worth: http://www.screencast.com/t/M9bDTHWzadqt

I tried using 'jsonp' as a dataType - that cured the console error, but then the script did nothing -pretty much as expected.

What am I missing here, or is js/post a waste of time?

Unfortunately, from what I understand, while I know you'd love to keep it JS-only, Javascript's same-origin policy might just not allow it. There are no funky tests we're doing on our end.

Even entering a message in the shoutbox itself doesn't prompt 'stored but invisible' messages from appearing - but there again if self-messaging is only being pushed to the Daniweb shoutbox via js, then that may explain it.

That's true, so that would be an explanation for that case.

Sorry, I replied before watching your screencast in full. It looks like it works but simply gives you an error message in the console that it was able to successfully post but couldn't retrieve any data back.

The DaniWeb shoutbox is working as expected in that it doesn't use polling to fetch one's own messages.

Member Avatar for diafol

Yep. Would other users see a message I posted from an app in my shoutbox? I'm assuming they would.

OK, it's usable. I'll have to check diff browsers and chat rooms. But looks like a js-only solution may be possible, as long as you don't mind the ajax error, which doesn't really affect the UX.

Would other users see a message I posted from an app in my shoutbox? I'm assuming they would.

Yes.

But looks like a js-only solution may be possible, as long as you don't mind the ajax error

Try doing a $.post request that does not have the .always(function(data) {}) part to it?? Since the actually POSTing worked, and it's just being finnicky about getting anything back, maybe it just doesn't like that you're trying to loop through an object sent back when it can't retrieve anything sent back?

Sorry, this isn't my forte. :(

Member Avatar for diafol

Sorry, this isn't my forte. :(

Not mine either :) Just trying to develop my skills.

The error's still there. Now changed ajax long form to post shortcut. No diff. Interestingly I seem to be getting a multiple poll error when I post a few msgs in rapid succession.

function postMsg()
{
    $.post('http://www.daniweb.com/api/chat',
        {'room_id': 120589,
         'message': $('#dw-postbox').val(),
         'room_type': 'member',
         'access_token': access_token}
    );
}

Don't know whether this would happen if other chatters were posting in quick succession.

//EDIT

Strangely - it seems to do this on occasion when just loading data (no posts made from app and none through shoutbox)

I think I know what race condition is causing it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.