1

I'm currently working on a new uptime command for my Twitch bot and I'm having problems with sockets. I'm trying to use this page http://api.twitch.tv/api/channels/duilio1337 to check if a streamer is online. It works fine sometimes but other times it reads the page header but not the page. Seems to be random.

alias uptimecheck {
  echo -a CHECKING CHANNEL $1 STATUS
  %uptimeurl = /api/channels/ $+ $1
  %uptimecheckchan = $1
  sockopen wuptime api.twitch.tv 80
}

on *:sockopen:wuptime: {
  sockwrite -n $sockname GET %uptimeurl HTTP/1.0
  sockwrite -n $sockname User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
  sockwrite -n $sockname Host: api.twitch.tv
  sockwrite -n $sockname Accept-Language: en-us
  sockwrite -n $sockname Accept: */*
  sockwrite -n $sockname
}

on *:sockread:wuptime: {
  if ($sockerr) {
    echo -a UPTIME Connection Error.
    halt
  }
  else {
    sockread -f %uptimeliverail
    if ($sockbr == 0) return
    ;echo -a %uptimeliverail
  }
}

on *:sockclose:wuptime: {
  var %TEMPliverail $mid(%uptimeliverail, $calc($pos(%uptimeliverail, "twitch_liverail_id") + 21), 4)
  if (%TEMPliverail == null) {
    set %uptimelive. $+ %uptimecheckchan 0
  }
  else if (%TEMPliverail isnum) {
    set %uptimelive. $+ %uptimecheckchan 1
  }
}
JosEduSol
  • 5,268
  • 3
  • 23
  • 31
Duilio
  • 11
  • 1

2 Answers2

0

The content of http://api.twitch.tv/api/channels/duilio1337 is just:

{"display_name":"duilio1337","game":null,"status":"Test","fight_ad_block":false,"_id":47890122,"name":"duilio1337","partner":false,"comscore_id":null,"comscore_c6":null,"twitch_liverail_id":null,"hide_ads_for_subscribers":false,"liverail_id":null,"ppv":false,"video_banner":null,"steam_id":null,"broadcaster_software":"unknown_rtmp","prerolls":true,"postrolls":true,"product_path":""}

It is not a HTML page, so what you mean with: it reads the page header but not the page. ?

Maybe you can explain better your problem.

Anyway... i think for your purposes in your sockopen event it should be enough to send GET and Host headers:

on *:sockopen:wuptime: {
  sockwrite -nt $sockname GET %uptimeurl HTTP/1.1
  sockwrite -nt $sockname Host: api.twitch.tv
  sockwrite $sockname $crlf                   
}
  • In HTTP/1.0 the Host is not needed, but doesn't hurt. In HTTP/1.1 is required.
  • The -t parameter is recommended to avoid & to be treated as binary variable.
  • The sockwrite $sockname $crlf is a more explicit way to finish the HTTP request, and is equivalent (IIRC) to your sockwrite -n $sockname (because -n adds a $crlf).

Also, your sockread event in general should look something like:

on *:sockread:wuptime: {
  if ($sockerr) {
    ;error msg code here
    return
  }

  var %data
  sockread %data

  ;your code here

  unset %data
}
  • Avoid halt, that command is used to immediately stop any further processing. In most cases is more appropriate to use return.
  • Avoid unnecessary if-else conditionals.

And is important to note, that each time the server replies with a line, the sockread event is initiated.

JosEduSol
  • 5,268
  • 3
  • 23
  • 31
0

The server is most likely returning

Vary: Accept Encoding

in the response header. Try replacing your Accept */* line with

sockwrite -n $sockname Accept: gzip

and see what happens. It will most likely return compressed binary data (rather than timing out, or returning nothing at all). The problem is, mIRC can't decompress this and doesn't support gzip decoding. I ran into this exact same problem last night and here is how I solved it for anyone that is working with mIRC sockets and needs to fetch gzip'd content.

First make sure you change the header like I have above.. to Accept: gzip

Second, grab a copy of 7za (7-zip for windows console) from http://www.7-zip.org/a/7za920.zip. Extract 7za and put it in the c:\program files (x86)\mIRC\ folder, or wherever your mirc.exe resides.

I've modified the OP's *on:sockread: block to fetch and extract the gzip'd content below:

on *:sockread:wuptime:{
  var %bare $rand(423539,999999)
  var %gz 1

  :play
  var %dg
  sockread %dg

  :wile
  if ( %gz = 2 ) {
    sockread -f &gzd
  }
  if ($sockerr) {
    return 
  }
  if ( %gz = 2 ) {
    while ($sockbr) {
      bwrite %bare -1 -1 &gzd
      sockread -f &gzd
    }
  }
  if ($sockbr == 0) {
    sockclose $sockname
    echo -at closed
    run -h 7za.exe e %bare
    .timer 1 2 wuptimeparse %bare
    return
  }
  if ( %dg = $null) {
    var %gz 2
    goto wile
  }

  goto play
}

This is probably pretty crude, but it starts bwriteing to a random file once the headers are finished returning, and once $sockbr is empty, it runs 7zip on the compressed binary data. The alias below will be called in a couple of seconds (time for 7zip to do its magic.. you can probably do this in a far more elegant way im sure) to spit out the plaintext JSON you're looking for. Be warned, if the JSON in your example is huge (4000+ characters) you will need to read it as a binvar with bread because the string will be too large to fit into a variable.

alias wuptimeparse {
  var %file $1
  var %bare $read(%file $+ ~,1)
  echo %bare
  ;do something with %bare
  .timer 1 1 remove %file
  .timer 1 1 remove %file $+ ~
}
fanfare
  • 1,827
  • 1
  • 21
  • 22